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