home *** CD-ROM | disk | FTP | other *** search
/ isnet Internet / Isnet Internet CD.iso / prog / hiz / 09 / 09.exe / adynware.exe / perl / lib / site / WWW / RobotRules.pm
Encoding:
Perl POD Document  |  1999-12-28  |  7.8 KB  |  342 lines

  1.  
  2. package WWW::RobotRules;
  3.  
  4. =head1 NAME
  5.  
  6. WWW::RobotsRules - Parse robots.txt files
  7.  
  8. =head1 SYNOPSIS
  9.  
  10.  require WWW::RobotRules;
  11.  my $robotsrules = new WWW::RobotRules 'MOMspider/1.0';
  12.  
  13.  use LWP::Simple qw(get);
  14.  
  15.  $url = "http://some.place/robots.txt";
  16.  my $robots_txt = get $url;
  17.  $robotsrules->parse($url, $robots_txt);
  18.  
  19.  $url = "http://some.other.place/robots.txt";
  20.  my $robots_txt = get $url;
  21.  $robotsrules->parse($url, $robots_txt);
  22.  
  23.  if($robotsrules->allowed($url)) {
  24.      $c = get $url;
  25.      ...
  26.  }
  27.  
  28. =head1 DESCRIPTION
  29.  
  30. This module parses a F<robots.txt> file as specified in
  31. "A Standard for Robot Exclusion", described in
  32. <URL:http://info.webcrawler.com/mak/projects/robots/norobots.html>
  33. Webmasters can use the F<robots.txt> file to disallow conforming
  34. robots access to parts of their WWW server.
  35.  
  36. The parsed file is kept in the WWW::RobotRules object, and this object
  37. provide methods to check if access to a given URL is prohibited.  The
  38. same WWW::RobotRules object can parse multiple F<robots.txt> files.
  39.  
  40. =head1 METHODS
  41.  
  42. =cut
  43.  
  44. $VERSION = sprintf("%d.%02d", q$Revision: 1.14 $ =~ /(\d+)\.(\d+)/);
  45. sub Version { $VERSION; }
  46.  
  47.  
  48. use URI::URL ();
  49. use strict;
  50.  
  51.  
  52. =head2 $rules = new WWW::RobotRules 'MOMspider/1.0'
  53.  
  54. This is the constructor for WWW::RobotRules objects.  The first 
  55. argument given to new() is the name of the robot. 
  56.  
  57. =cut
  58.  
  59. sub new {
  60.     my($class, $ua) = @_;
  61.  
  62.     $class = "WWW::RobotRules::InCore" if $class eq "WWW::RobotRules";
  63.  
  64.     my $self = bless { }, $class;
  65.     $self->agent($ua);
  66.     $self;
  67. }
  68.  
  69.  
  70. =head2 $rules->parse($url, $content, $fresh_until)
  71.  
  72. The parse() method takes as arguments the URL that was used to
  73. retrieve the F</robots.txt> file, and the contents of the file.
  74.  
  75. =cut
  76.  
  77. sub parse {
  78.     my($self, $url, $txt, $fresh_until) = @_;
  79.  
  80.     $url = new URI::URL $url unless ref($url);    # make it URL
  81.     my $netloc = $url->netloc;
  82.  
  83.     $self->clear_rules($netloc);
  84.     $self->fresh_until($netloc, $fresh_until || (time + 365*24*3600));
  85.  
  86.     my $ua;
  87.     my $is_me = 0;        # 1 iff this record is for me
  88.     my $is_anon = 0;        # 1 iff this record is for *
  89.     my @me_disallowed = ();    # rules disallowed for me
  90.     my @anon_disallowed = ();    # rules disallowed for *
  91.  
  92.     for(split(/\n/, $txt)) {
  93.     s/\015$//g;
  94.  
  95.     next if /^\s*\#/;
  96.  
  97.     s/\s*\#.*//;        # remove comments at end-of-line
  98.  
  99.     if (/^\s*$/) {        # blank line
  100.         last if $is_me; # That was our record. No need to read the rest.
  101.         $is_anon = 0;
  102.     }
  103.         elsif (/^User-Agent:\s*(.*)/i) {
  104.         $ua = $1;
  105.         $ua =~ s/\s+$//;
  106.         if ($is_me) {
  107.         }
  108.         elsif ($ua eq '*') {
  109.         $is_anon = 1;
  110.         }
  111.         elsif($self->is_me($ua)) {
  112.         $is_me = 1;
  113.         }
  114.     }
  115.     elsif (/^Disallow:\s*(.*)/i) {
  116.         unless (defined $ua) {
  117.         warn "RobotRules: Disallow without preceding User-agent\n";
  118.         $is_anon = 1;  # assume that User-agent: * was intended
  119.         }
  120.         my $disallow = $1;
  121.         $disallow =~ s/\s+$//;
  122.         if (length $disallow) {
  123.         $disallow = URI::URL->new($disallow, $url)->full_path;
  124.         }
  125.  
  126.         if ($is_me) {
  127.         push(@me_disallowed, $disallow);
  128.         }
  129.         elsif ($is_anon) {
  130.         push(@anon_disallowed, $disallow);
  131.         }
  132.     }
  133.     else {
  134.         warn "RobotRules: Unexpected line: $_\n";
  135.     }
  136.     }
  137.  
  138.     if ($is_me) {
  139.     $self->push_rules($netloc, @me_disallowed);
  140.     } else {
  141.     $self->push_rules($netloc, @anon_disallowed);
  142.     }
  143. }
  144.  
  145. sub is_me {
  146.     my($self, $ua) = @_;
  147.     my $me = $self->agent;
  148.     return index(lc($ua), lc($me)) >= 0;
  149. }
  150.  
  151. =head2 $rules->allowed($url)
  152.  
  153. Returns TRUE if this robot is allowed to retrieve this URL.
  154.  
  155. =cut
  156.  
  157. sub allowed {
  158.     my($self, $url) = @_;
  159.     $url = URI::URL->new($url) unless ref $url;    # make it URL
  160.  
  161.     my $netloc = $url->netloc;
  162.  
  163.     my $fresh_until = $self->fresh_until($netloc);
  164.     return -1 if !defined($fresh_until) || $fresh_until < time;
  165.  
  166.     my $str = $url->full_path;
  167.     my $rule;
  168.     for $rule ($self->rules($netloc)) {
  169.     return 1 unless length $rule;
  170.     return 0 if index($str, $rule) == 0;
  171.     }
  172.     return 1;
  173. }
  174.  
  175. sub agent;
  176. sub visit;
  177. sub no_visits;
  178. sub last_visits;
  179. sub fresh_until;
  180. sub push_rules;
  181. sub clear_rules;
  182. sub rules;
  183. sub dump;
  184.  
  185. package WWW::RobotRules::InCore;
  186.  
  187. use vars qw(@ISA);
  188. @ISA = qw(WWW::RobotRules);
  189.  
  190. =head2 $rules->agent([$name])
  191.  
  192. Get/set the agent name. NOTE: Changing the agent name will clear the robots.txt
  193. rules and expire times out of the cache.
  194.  
  195. =cut
  196.  
  197. sub agent {
  198.     my ($self, $name) = @_;
  199.     my $old = $self->{'ua'};
  200.     if ($name) {
  201.     delete $self->{'loc'};   # all old info is now stale
  202.     $name =~ s!/?\s*\d+.\d+\s*$!!;  # loose version
  203.     $self->{'ua'}=$name;
  204.     }
  205.     $old;
  206. }
  207.  
  208. sub visit {
  209.     my($self, $netloc, $time) = @_;
  210.     $time ||= time;
  211.     $self->{'loc'}{$netloc}{'last'} = $time;
  212.     
  213.     my $count = \$self->{'loc'}{$netloc}{'count'};
  214.     if (!defined $$count) {
  215.     $$count = 1;
  216.     } else {
  217.     $$count++;
  218.     }
  219. }
  220.  
  221. sub no_visits {
  222.     my ($self, $netloc) = @_;
  223.     $self->{'loc'}{$netloc}{'count'};
  224. }
  225.  
  226. sub last_visit {
  227.     my ($self, $netloc) = @_;
  228.     $self->{'loc'}{$netloc}{'last'};
  229. }
  230.  
  231. sub fresh_until {
  232.     my ($self, $netloc, $fresh_until) = @_;
  233.     my $old = $self->{'loc'}{$netloc}{'fresh'};
  234.     if (defined $fresh_until) {
  235.     $self->{'loc'}{$netloc}{'fresh'} = $fresh_until;
  236.     }
  237.     $old;
  238. }
  239.  
  240. sub push_rules {
  241.     my($self, $netloc, @rules) = @_;
  242.     push (@{$self->{'loc'}{$netloc}{'rules'}}, @rules);
  243. }
  244.  
  245. sub clear_rules {
  246.     my($self, $netloc) = @_;
  247.     delete $self->{'loc'}{$netloc}{'rules'};
  248. }
  249.  
  250. sub rules {
  251.     my($self, $netloc) = @_;
  252.     if (defined $self->{'loc'}{$netloc}{'rules'}) {
  253.     return @{$self->{'loc'}{$netloc}{'rules'}};
  254.     } else {
  255.     return ();
  256.     }
  257. }
  258.  
  259. sub dump
  260. {
  261.     my $self = shift;
  262.     for (keys %$self) {
  263.     next if $_ eq 'loc';
  264.     print "$_ = $self->{$_}\n";
  265.     }
  266.     for (keys %{$self->{'loc'}}) {
  267.     my @rules = $self->rules($_);
  268.     print "$_: ", join("; ", @rules), "\n";
  269.     
  270.     }
  271. }
  272.  
  273. 1;
  274.  
  275. __END__
  276.  
  277. =head1 ROBOTS.TXT
  278.  
  279. The format and semantics of the "/robots.txt" file are as follows
  280. (this is an edited abstract of
  281. <URL:http://info.webcrawler.com/mak/projects/robots/norobots.html>):
  282.  
  283. The file consists of one or more records separated by one or more
  284. blank lines. Each record contains lines of the form
  285.  
  286.   <field-name>: <value>
  287.  
  288. The field name is case insensitive.  Text after the '#' character on a
  289. line is ignored during parsing.  This is used for comments.  The
  290. following <field-names> can be used:
  291.  
  292. =over 3
  293.  
  294. =item User-Agent
  295.  
  296. The value of this field is the name of the robot the record is
  297. describing access policy for.  If more than one I<User-Agent> field is
  298. present the record describes an identical access policy for more than
  299. one robot. At least one field needs to be present per record.  If the
  300. value is '*', the record describes the default access policy for any
  301. robot that has not not matched any of the other records.
  302.  
  303. =item Disallow
  304.  
  305. The value of this field specifies a partial URL that is not to be
  306. visited. This can be a full path, or a partial path; any URL that
  307. starts with this value will not be retrieved
  308.  
  309. =back
  310.  
  311. =head2 Examples
  312.  
  313. The following example "/robots.txt" file specifies that no robots
  314. should visit any URL starting with "/cyberworld/map/" or "/tmp/":
  315.  
  316.  
  317.   User-agent: *
  318.   Disallow: /cyberworld/map/ # This is an infinite virtual URL space
  319.   Disallow: /tmp/ # these will soon disappear
  320.  
  321. This example "/robots.txt" file specifies that no robots should visit
  322. any URL starting with "/cyberworld/map/", except the robot called
  323. "cybermapper":
  324.  
  325.  
  326.   User-agent: *
  327.   Disallow: /cyberworld/map/ # This is an infinite virtual URL space
  328.  
  329.   User-agent: cybermapper
  330.   Disallow:
  331.  
  332. This example indicates that no robots should visit this site further:
  333.  
  334.   User-agent: *
  335.   Disallow: /
  336.  
  337. =head1 SEE ALSO
  338.  
  339. L<LWP::RobotUA>, L<WWW::RobotRules::AnyDBM_File>
  340.  
  341. =cut
  342.