home *** CD-ROM | disk | FTP | other *** search
/ Australian Personal Computer 2004 July / APC0407D2.iso / workshop / apache / files / ActivePerl-5.6.1.638-MSWin32-x86.msi / _ee8c97d2a50a4594985f036906862c88 < prev    next >
Encoding:
Text File  |  2004-04-13  |  7.9 KB  |  303 lines

  1. package LWP::RobotUA;
  2.  
  3. # $Id: RobotUA.pm,v 1.23 2003/10/24 11:13:03 gisle Exp $
  4.  
  5. require LWP::UserAgent;
  6. @ISA = qw(LWP::UserAgent);
  7. $VERSION = sprintf("%d.%02d", q$Revision: 1.23 $ =~ /(\d+)\.(\d+)/);
  8.  
  9. require WWW::RobotRules;
  10. require HTTP::Request;
  11. require HTTP::Response;
  12.  
  13. use Carp ();
  14. use LWP::Debug ();
  15. use HTTP::Status ();
  16. use HTTP::Date qw(time2str);
  17. use strict;
  18.  
  19. #
  20. # Additional attributes in addition to those found in LWP::UserAgent:
  21. #
  22. # $self->{'delay'}    Required delay between request to the same
  23. #                     server in minutes.
  24. #
  25. # $self->{'rules'}     A WWW::RobotRules object
  26. #
  27.  
  28. sub new
  29. {
  30.     my($class,$name,$from,$rules) = @_;
  31.  
  32.     Carp::croak('LWP::RobotUA name required') unless $name;
  33.     Carp::croak('LWP::RobotUA from address required') unless $from
  34.      and $from =~ m/\@/;
  35.  
  36.     my $self = new LWP::UserAgent;
  37.     $self = bless $self, $class;
  38.  
  39.     $self->{'delay'} = 1;   # minutes
  40.     $self->{'agent'} = $name;
  41.     $self->{'from'}  = $from;
  42.     $self->{'use_sleep'} = 1;
  43.  
  44.     if ($rules) {
  45.     $rules->agent($name);
  46.     $self->{'rules'} = $rules;
  47.     }
  48.     else {
  49.     $self->{'rules'} = new WWW::RobotRules $name;
  50.     }
  51.  
  52.     $self;
  53. }
  54.  
  55.  
  56. sub delay     { shift->_elem('delay',     @_); }
  57. sub use_sleep { shift->_elem('use_sleep', @_); }
  58.  
  59.  
  60. sub agent
  61. {
  62.     my $self = shift;
  63.     my $old = $self->SUPER::agent(@_);
  64.     if (@_) {
  65.     # Changing our name means to start fresh
  66.     $self->{'rules'}->agent($self->{'agent'}); 
  67.     }
  68.     $old;
  69. }
  70.  
  71.  
  72. sub rules {
  73.     my $self = shift;
  74.     my $old = $self->_elem('rules', @_);
  75.     $self->{'rules'}->agent($self->{'agent'}) if @_;
  76.     $old;
  77. }
  78.  
  79. sub no_visits
  80. {
  81.     my($self, $netloc) = @_;
  82.     $self->{'rules'}->no_visits($netloc) || 0;
  83. }
  84.  
  85. *host_count = \&no_visits;  # backwards compatibility with LWP-5.02
  86.  
  87.  
  88. sub host_wait
  89. {
  90.     my($self, $netloc) = @_;
  91.     return undef unless defined $netloc;
  92.     my $last = $self->{'rules'}->last_visit($netloc);
  93.     if ($last) {
  94.     my $wait = int($self->{'delay'} * 60 - (time - $last));
  95.     $wait = 0 if $wait < 0;
  96.     return $wait;
  97.     }
  98.     return 0;
  99. }
  100.  
  101.  
  102. sub simple_request
  103. {
  104.     my($self, $request, $arg, $size) = @_;
  105.  
  106.     LWP::Debug::trace('()');
  107.  
  108.     # Do we try to access a new server?
  109.     my $allowed = $self->{'rules'}->allowed($request->url);
  110.  
  111.     if ($allowed < 0) {
  112.     LWP::Debug::debug("Host is not visited before, or robots.txt expired.");
  113.     # fetch "robots.txt"
  114.     my $robot_url = $request->url->clone;
  115.     $robot_url->path("robots.txt");
  116.     $robot_url->query(undef);
  117.     LWP::Debug::debug("Requesting $robot_url");
  118.  
  119.     # make access to robot.txt legal since this will be a recursive call
  120.     $self->{'rules'}->parse($robot_url, ""); 
  121.  
  122.     my $robot_req = new HTTP::Request 'GET', $robot_url;
  123.     my $robot_res = $self->request($robot_req);
  124.     my $fresh_until = $robot_res->fresh_until;
  125.     if ($robot_res->is_success) {
  126.         my $c = $robot_res->content;
  127.         if ($robot_res->content_type =~ m,^text/, && $c =~ /Disallow/) {
  128.         LWP::Debug::debug("Parsing robot rules");
  129.         $self->{'rules'}->parse($robot_url, $c, $fresh_until);
  130.         }
  131.         else {
  132.         LWP::Debug::debug("Ignoring robots.txt");
  133.         $self->{'rules'}->parse($robot_url, "", $fresh_until);
  134.         }
  135.  
  136.     }
  137.     else {
  138.         LWP::Debug::debug("No robots.txt file found");
  139.         $self->{'rules'}->parse($robot_url, "", $fresh_until);
  140.     }
  141.  
  142.     # recalculate allowed...
  143.     $allowed = $self->{'rules'}->allowed($request->url);
  144.     }
  145.  
  146.     # Check rules
  147.     unless ($allowed) {
  148.     my $res = new HTTP::Response
  149.       &HTTP::Status::RC_FORBIDDEN, 'Forbidden by robots.txt';
  150.     $res->request( $request ); # bind it to that request
  151.     return $res;
  152.     }
  153.  
  154.     my $netloc = eval { local $SIG{__DIE__}; $request->url->host_port; };
  155.     my $wait = $self->host_wait($netloc);
  156.  
  157.     if ($wait) {
  158.     LWP::Debug::debug("Must wait $wait seconds");
  159.     if ($self->{'use_sleep'}) {
  160.         sleep($wait)
  161.     }
  162.     else {
  163.         my $res = new HTTP::Response
  164.           &HTTP::Status::RC_SERVICE_UNAVAILABLE, 'Please, slow down';
  165.         $res->header('Retry-After', time2str(time + $wait));
  166.         $res->request( $request ); # bind it to that request
  167.         return $res;
  168.     }
  169.     }
  170.  
  171.     # Perform the request
  172.     my $res = $self->SUPER::simple_request($request, $arg, $size);
  173.  
  174.     $self->{'rules'}->visit($netloc);
  175.  
  176.     $res;
  177. }
  178.  
  179.  
  180. sub as_string
  181. {
  182.     my $self = shift;
  183.     my @s;
  184.     push(@s, "Robot: $self->{'agent'} operated by $self->{'from'}  [$self]");
  185.     push(@s, "    Minimum delay: " . int($self->{'delay'}*60) . "s");
  186.     push(@s, "    Will sleep if too early") if $self->{'use_sleep'};
  187.     push(@s, "    Rules = $self->{'rules'}");
  188.     join("\n", @s, '');
  189. }
  190.  
  191. 1;
  192.  
  193.  
  194. __END__
  195.  
  196. =head1 NAME
  197.  
  198. LWP::RobotUA - a class for well-behaved Web robots
  199.  
  200. =head1 SYNOPSIS
  201.  
  202.   use LWP::RobotUA;
  203.   my $ua = LWP::RobotUA->new('my-robot/0.1', 'me@foo.com');
  204.   $ua->delay(10);  # be very nice -- max one hit every ten minutes!
  205.   ...
  206.  
  207.   # Then just use it just like a normal LWP::UserAgent:
  208.   my $response = $ua->get('http://whatever.int/...');
  209.   ...
  210.  
  211. =head1 DESCRIPTION
  212.  
  213. This class implements a user agent that is suitable for robot
  214. applications.  Robots should be nice to the servers they visit.  They
  215. should consult the F</robots.txt> file to ensure that they are welcomed
  216. and they should not make requests too frequently.
  217.  
  218. But before you consider writing a robot, take a look at
  219. <URL:http://www.robotstxt.org/>.
  220.  
  221. When you use a I<LWP::RobotUA> object as your user agent, then you do not
  222. really have to think about these things yourself; C<robots.txt> files
  223. are automatically consulted and obeyed, the server isn't queried
  224. too rapidly, and so on.  Just send requests
  225. as you do when you are using a normal I<LWP::UserAgent>
  226. object (using C<< $ua->get(...) >>, C<< $ua->head(...) >>,
  227. C<< $ua->request(...) >>, etc.), and this
  228. special agent will make sure you are nice.
  229.  
  230. =head1 METHODS
  231.  
  232. The LWP::RobotUA is a sub-class of LWP::UserAgent and implements the
  233. same methods. In addition the following methods are provided:
  234.  
  235. =over 4
  236.  
  237. =item $ua = LWP::RobotUA->new( $agent_name, $from )
  238.  
  239. =item $ua = LWP::RobotUA->new( $agent_name, $from, $rules )
  240.  
  241. Your robot's name and the mail address of the human responsible for
  242. the robot (i.e. you) are required by the constructor.
  243.  
  244. Optionally it allows you to specify the I<WWW::RobotRules> object to
  245. use.  If you don't provide one, then this user agent will make its own
  246. internal database of F<robots.txt> rules as needed.
  247.  
  248. =item $ua->delay
  249.  
  250. =item $ua->delay( $minutes )
  251.  
  252. Get/set the minimum delay between requests to the same server, in
  253. I<minutes>.  The default is 1 minute.  Note that this number doesn't
  254. have to be an integer; for example, this sets the delay to 10 seconds:
  255.  
  256.     $ua->delay(10/60);
  257.  
  258. =item $ua->use_sleep
  259.  
  260. =item $ua->use_sleep( $boolean )
  261.  
  262. Get/set a value indicating whether the UA should sleep() if requests
  263. arrive too fast, defined as $ua->delay minutes not passed since
  264. last request to the given server.  The default is TRUE.  If this value is
  265. FALSE then an internal SERVICE_UNAVAILABLE response will be generated.
  266. It will have an Retry-After header that indicates when it is OK to
  267. send another request to this server.
  268.  
  269. =item $ua->rules
  270.  
  271. =item $ua->rules( $rules )
  272.  
  273. Set/get which I<WWW::RobotRules> object to use.
  274.  
  275. =item $ua->no_visits( $netloc )
  276.  
  277. Returns the number of documents fetched from this server host. Yeah I
  278. know, this method should probably have been named num_visits() or
  279. something like that. :-(
  280.  
  281. =item $ua->host_wait( $netloc )
  282.  
  283. Returns the number of I<seconds> (from now) you must wait before you can
  284. make a new request to this host.
  285.  
  286. =item $ua->as_string
  287.  
  288. Returns a string that describes the state of the UA.
  289. Mainly useful for debugging.
  290.  
  291. =back
  292.  
  293. =head1 SEE ALSO
  294.  
  295. L<LWP::UserAgent>, L<WWW::RobotRules>
  296.  
  297. =head1 COPYRIGHT
  298.  
  299. Copyright 1996-2002 Gisle Aas.
  300.  
  301. This library is free software; you can redistribute it and/or
  302. modify it under the same terms as Perl itself.
  303.