home *** CD-ROM | disk | FTP | other *** search
/ isnet Internet / Isnet Internet CD.iso / prog / hiz / 09 / 09.exe / adynware.exe / perl / lib / site / LWP / RobotUA.pm < prev    next >
Encoding:
Perl POD Document  |  1999-12-28  |  5.7 KB  |  254 lines

  1.  
  2. package LWP::RobotUA;
  3.  
  4. require LWP::UserAgent;
  5. @ISA = qw(LWP::UserAgent);
  6.  
  7. require WWW::RobotRules;
  8. require HTTP::Request;
  9. require HTTP::Response;
  10.  
  11. use Carp ();
  12. use LWP::Debug ();
  13. use HTTP::Status ();
  14. use HTTP::Date qw(time2str);
  15. use strict;
  16.  
  17. =head1 NAME
  18.  
  19. LWP::RobotUA - A class for Web Robots
  20.  
  21. =head1 SYNOPSIS
  22.  
  23.   require LWP::RobotUA;
  24.   $ua = new LWP::RobotUA 'my-robot/0.1', 'me@foo.com';
  25.   $ua->delay(10);  # be very nice, go slowly
  26.   ...
  27.   $res = $ua->request($req);
  28.  
  29. =head1 DESCRIPTION
  30.  
  31. This class implements a user agent that is suitable for robot
  32. applications.  Robots should be nice to the servers they visit.  They
  33. should consult the F<robots.txt> file to ensure that they are welcomed
  34. and they should not send too frequent requests.
  35.  
  36. But, before you consider writing a robot take a look at
  37. <URL:http://info.webcrawler.com/mak/projects/robots/robots.html>.
  38.  
  39. When you use a I<LWP::RobotUA> as your user agent, then you do not
  40. really have to think about these things yourself.  Just send requests
  41. as you do when you are using a normal I<LWP::UserAgent> and this
  42. special agent will make sure you are nice.
  43.  
  44. =head1 METHODS
  45.  
  46. The LWP::RobotUA is a sub-class of LWP::UserAgent and implements the
  47. same methods.  The use_alarm() method also desides whether we will
  48. wait if a request is tried too early (if true), or will return an error
  49. response (if false).
  50.  
  51. In addition these methods are provided:
  52.  
  53. =cut
  54.  
  55.  
  56.  
  57.  
  58. =head2 $ua = LWP::RobotUA->new($agent_name, $from, [$rules])
  59.  
  60. Your robot's name and the mail address of the human responsible for
  61. the robot (i.e. you) is required by the constructor.
  62.  
  63. Optionally it allows you to specify the I<WWW::RobotRules> object to
  64. use.
  65.  
  66. =cut
  67.  
  68. sub new
  69. {
  70.     my($class,$name,$from,$rules) = @_;
  71.  
  72.     Carp::croak('LWP::RobotUA name required') unless $name;
  73.     Carp::croak('LWP::RobotUA from address required') unless $from;
  74.  
  75.     my $self = new LWP::UserAgent;
  76.     $self = bless $self, $class;
  77.  
  78.     $self->{'delay'} = 1;   # minutes
  79.     $self->{'agent'} = $name;
  80.     $self->{'from'}  = $from;
  81.  
  82.     if ($rules) {
  83.     $rules->agent($name);
  84.     $self->{'rules'} = $rules;
  85.     } else {
  86.     $self->{'rules'} = new WWW::RobotRules $name;
  87.     }
  88.  
  89.     $self;
  90. }
  91.  
  92.  
  93. =head2 $ua->delay([$minutes])
  94.  
  95. Set the minimum delay between requests to the same server.  The
  96. default is 1 minute.
  97.  
  98. =cut
  99.  
  100. sub delay { shift->_elem('delay', @_); }
  101.  
  102.  
  103. sub agent
  104. {
  105.     my $self = shift;
  106.     my $old = $self->SUPER::agent(@_);
  107.     if (@_) {
  108.     $self->{'rules'}->agent($self->{'agent'}); 
  109.     }
  110.     $old;
  111. }
  112.  
  113.  
  114. =head2 $ua->rules([$rules])
  115.  
  116. Set/get which I<WWW::RobotRules> object to use. 
  117.  
  118. =cut
  119.  
  120. sub rules {
  121.     my $self = shift;
  122.     my $old = $self->_elem('rules', @_);
  123.     $self->{'rules'}->agent($self->{'agent'}) if @_;
  124.     $old;
  125. }
  126.  
  127.  
  128. =head2 $ua->no_visits($netloc)
  129.  
  130. Returns the number of documents fetched from this server host.
  131.  
  132. =cut
  133.  
  134. sub no_visits
  135. {
  136.     my($self, $netloc) = @_;
  137.     $self->{'rules'}->no_visits($netloc);
  138. }
  139.  
  140. *host_count = \&no_visits;  # backwards compatibility with LWP-5.02
  141.  
  142.  
  143. =head2 $ua->host_wait($netloc)
  144.  
  145. Returns the number of seconds you must wait before you can make a new
  146. request to this host.
  147.  
  148. =cut
  149.  
  150. sub host_wait
  151. {
  152.     my($self, $netloc) = @_;
  153.     return undef unless defined $netloc;
  154.     my $last = $self->{'rules'}->last_visit($netloc);
  155.     if ($last) {
  156.     my $wait = int($self->{'delay'} * 60 - (time - $last));
  157.     $wait = 0 if $wait < 0;
  158.     return $wait;
  159.     }
  160.     return 0;
  161. }
  162.  
  163.  
  164. sub simple_request
  165. {
  166.     my($self, $request, $arg, $size) = @_;
  167.  
  168.     LWP::Debug::trace('()');
  169.  
  170.     my $allowed = $self->{'rules'}->allowed($request->url);
  171.  
  172.     if ($allowed < 0) {
  173.     LWP::Debug::debug("Host is not visited before, or robots.txt expired.");
  174.     my $robot_url = $request->url->clone;
  175.     $robot_url->path("robots.txt");
  176.     $robot_url->params(undef);
  177.     $robot_url->query(undef);
  178.     LWP::Debug::debug("Requesting $robot_url");
  179.  
  180.     $self->{'rules'}->parse($robot_url, ""); 
  181.  
  182.     my $robot_req = new HTTP::Request 'GET', $robot_url;
  183.     my $robot_res = $self->request($robot_req);
  184.     my $fresh_until = $robot_res->fresh_until;
  185.     if ($robot_res->is_success) {
  186.         LWP::Debug::debug("Parsing robot rules");
  187.         $self->{'rules'}->parse($robot_url, $robot_res->content, 
  188.                     $fresh_until);
  189.     } else {
  190.         LWP::Debug::debug("No robots.txt file found");
  191.         $self->{'rules'}->parse($robot_url, "", $fresh_until);
  192.     }
  193.  
  194.     $allowed = $self->{'rules'}->allowed($request->url);
  195.     }
  196.  
  197.     unless ($allowed) {
  198.     return new HTTP::Response
  199.       &HTTP::Status::RC_FORBIDDEN, 'Forbidden by robots.txt';
  200.     }
  201.  
  202.     my $netloc = $request->url->netloc;
  203.     my $wait = $self->host_wait($netloc);
  204.  
  205.     if ($wait) {
  206.     LWP::Debug::debug("Must wait $wait seconds");
  207.     if ($self->{'use_alarm'}) {
  208.         sleep($wait)
  209.     } else {
  210.         my $res = new HTTP::Response
  211.           &HTTP::Status::RC_SERVICE_UNAVAILABLE, 'Please, slow down';
  212.         $res->header('Retry-After', time2str(time + $wait));
  213.         return $res;
  214.     }
  215.     }
  216.  
  217.     my $res = $self->SUPER::simple_request($request, $arg, $size);
  218.  
  219.     $self->{'rules'}->visit($netloc);
  220.  
  221.     $res;
  222. }
  223.  
  224.  
  225. =head2 $ua->as_string
  226.  
  227. Returns a text that describe the state of the UA.
  228. Mainly useful for debugging.
  229.  
  230. =cut
  231.  
  232. sub as_string
  233. {
  234.     my $self = shift;
  235.     my @s;
  236.     push(@s, "Robot: $self->{'agent'} operated by $self->{'from'}  [$self]");
  237.     push(@s, "    Minimum delay: " . int($self->{'delay'}*60) . "s");
  238.     push(@s, "    Will sleep if too early") if $self->{'use_alarm'};
  239.     push(@s, "    Rules = $self->{'rules'}");
  240.     join("\n", @s, '');
  241. }
  242.  
  243. 1;
  244.  
  245. =head1 SEE ALSO
  246.  
  247. L<LWP::UserAgent>, L<WWW::RobotRules>
  248.  
  249. =head1 AUTHOR
  250.  
  251. Gisle Aas E<lt>aas@sn.no>
  252.  
  253. =cut
  254.