何個か前のエントリーで書いた、ページ内のリンクを辿って行く機能をGunghoX::FollowLinksで実装したよ。
まだちょっといけてない部分があるので多少の書き直しはあると思うけど、とりあえずうpしたさ。使い方はこんな感じ。このクローラーはあるURL(例:http://www.example.com/)を与えると、そのURLから辿れる、そのURL階層以下のページを全部取ってくるデス。長いからエントリーの後のほうでで説明もつけますよ
まずGunghoを普通にセットアップ。23行目から47行目はproviderとhandlerのセットアップしてます。Gungho 0.09003からGungho::Inlineを使わなくてもprovider/handlerを別クラスで定義する必要はないので、普通に関数を定義してそのまま渡してます。動作的には、ただ最初にもらったURLから始まって、その後$p->requestsに入れられるURLを端から取りに行くproviderと、とってきたURLをprintするhandlerというだけ。
48行目から使うコンポーネントの定義。ここではURLを辿って行くためのGunghoX::FollowLinksと、robots.txtを勝手に処理してくれるRobotRules、それにアクセス数を単純にカウントアップして、一定時間以内にアクセス数を設定値以上にいかないように抑えるThrottle::Simpleを使います。
53行目からはコンポーネント毎の定義。まずThrottle::Simple。3600秒以内に1000件のリクエストを最大値として設定
59行目からGunghoX::FollowLinksの設定。HTMLパーサーを使う。merge_rule => "ALL"で、これから定義するルールの全てがFOLLOW_ALLOWにならないとリンクを辿るのを許さない。上から見て行くと、
providerとhandler以外はYAMLで書く事もできるけど、今回はとりあえずそのままPerlで書いてみた。
まだ使い方はこれで決定というわけではないのでご要望等ありましたらブクマなりコメントなりでご意見をくださいまし。
まだちょっといけてない部分があるので多少の書き直しはあると思うけど、とりあえずうpしたさ。使い方はこんな感じ。このクローラーはあるURL(例:http://www.example.com/)を与えると、そのURLから辿れる、そのURL階層以下のページを全部取ってくるデス。長いからエントリーの後のほうでで説明もつけますよ
#!/usr/local/bin/perl
# $Id$
#
# Copyright (c) 2007 Daisuke Maki <daisuke@endeworks.jp>
# All rights reserved.
use strict;
use warnings;
use Gungho;
use Gungho::Request;
use GunghoX::FollowLinks;
use URI;
main();
sub main
{
my $site = $ARGV[0];
$site = URI->new($site) unless eval { $site->isa('URI') } && !$@;
Gungho->run({
provider => sub {
my ($p, $c) = @_;
if (! $p->{started}) {
$c->send_request( Gungho::Request->new( GET => $site ) );
$p->{started} = 1;
} else {
my $requests = $p->requests;
while (my $request = shift @$requests) {
$request->uri->fragment(undef);
# Make sure to use the original hostname
my $original_uri = $request->original_uri;
if ( $p->{seen}{$original_uri->as_string}++ ) {
next;
}
$c->send_request( $request );
}
}
return 1;
},
handler => sub {
my ($h, $c, $req, $res) = @_;
$c->follow_links($res);
print STDERR "Fetched ", $res->request->uri->as_string, "\n";
},
components => [
'+GunghoX::FollowLinks',
'RobotRules',
'Throttle::Simple',
],
throttle => {
simple => {
max_items => 1000,
interval => 3600,
}
},
follow_links => {
parsers => [
{ module => "HTML",
config => {
merge_rule => "ALL",
rules => [
{ module => "HTML::SelectedTags",
config => {
tags => [ qw(a link) ]
}
},
{ module => "URI",
config => {
match => [ {
scheme => qr/^http$/i,
host => $site->host,
path => "^" . ($site->path || "/"),
action_nomatch => "FOLLOW_DENY"
} ]
}
},
{ module => "MIME",
config => {
types => [ qw(text/html) ],
unknown => "FOLLOW_ALLOW",
}
},
]
}
}
]
}
});
}
まずGunghoを普通にセットアップ。23行目から47行目はproviderとhandlerのセットアップしてます。Gungho 0.09003からGungho::Inlineを使わなくてもprovider/handlerを別クラスで定義する必要はないので、普通に関数を定義してそのまま渡してます。動作的には、ただ最初にもらったURLから始まって、その後$p->requestsに入れられるURLを端から取りに行くproviderと、とってきたURLをprintするhandlerというだけ。
48行目から使うコンポーネントの定義。ここではURLを辿って行くためのGunghoX::FollowLinksと、robots.txtを勝手に処理してくれるRobotRules、それにアクセス数を単純にカウントアップして、一定時間以内にアクセス数を設定値以上にいかないように抑えるThrottle::Simpleを使います。
53行目からはコンポーネント毎の定義。まずThrottle::Simple。3600秒以内に1000件のリクエストを最大値として設定
59行目からGunghoX::FollowLinksの設定。HTMLパーサーを使う。merge_rule => "ALL"で、これから定義するルールの全てがFOLLOW_ALLOWにならないとリンクを辿るのを許さない。上から見て行くと、
- aタグかlinkタグで
- URLがhttpで、ホストがスクリプトとして与えたURLのホストと一緒、なおかつ階層はそのURLの下で
- URLから推測されるMIMEタイプがHTMLであること(わからない場合はとりあえず辿る事を許す)
providerとhandler以外はYAMLで書く事もできるけど、今回はとりあえずそのままPerlで書いてみた。
まだ使い方はこれで決定というわけではないのでご要望等ありましたらブクマなりコメントなりでご意見をくださいまし。
コメント