ブログが続かないわけ

この日記のはてなブックマーク数
Webエンジニアが思うこと by junichiro on Facebook

Web::Scraper を利用して画像をたくさん手に入れるWeb::Collector なんてものを書いた

このエントリーを含むはてなブックマーク hateb

■主な機能

1. YAML を書くだけで簡単に大量の画像を手に入れることができる。
2. 過去に取得した画像(ファイル名でチェック)は取得しない。
3. たどるページ数を制限できる。

1. がこのモジュールの主旨。
2. の機能があるおかげで、取得した画像を整理したり、いらないものを削除したりしても、もう一度同じ画像を取得してしまうことがない。
3. 最初に取得するときはたくさんのページをたどるようにしてスクリプトをキックし、その後例えばcron で定期的にまわす場合などは、たどるページを1とかにすると相手のサーバにも負担をかけなくて良い。

■使い方

1. YAMLを書く (config.yaml)
---

img_dir: /Users/junichiro/Pictures/e3
uri: http://www.e-3.ne.jp/image?p=
max_cnt: 1
process: process "p.member>a>img", 'img_uri[]' => [ '@src', sub { s/80x80/640x640/ } ]

こんな感じ

2. スクリプトを実行する
./collector.pl config.yaml

これだけ。これをcron でまわせば知らないうちに大量の画像をgetできる。

■設定ファイル(yaml)の簡単な解説

img_dir

これは画像を格納するディレクトリ。僕の場合、ここは一時的に取得するディレクトリにしておいて、取得した画像を整理した後は空っぽにして使っている。ただし、このディレクトリにある.already.yaml というファイルで、すでに取得した画像のリストを保持しているので、これは削除しないように注意してほしい。逆に、画像の再取得をしたいときは、.already.yaml をいじればよい。

uri

取得したサイトのURIの断片
あまり汎用性が無くて申し訳ないのだが、ページを制御する変数がURIの中にわかりやすく含まれている場合しか使えない。この例ではp=というところでページを制御している。汎用性は低いが、この形で結構行けるサイトも多かった。p=とかpage=とかid=とかpage_id=とか、こんな感じになっているのが多い。

max_cnt

たどるページ数
最初のうちは1とかで試すといい。

process

画像を取得するためのXPath でも書けばよろし。
今回の例では、こんな形のHTMLから画像を取得できる。
<p class="member">
<a href="/mypage/diary/detail/7370">
<img src="http://img01.e-3.ne.jp/diary/10000/80x80/0_2.jpg" class="image_list" alt="" />
</a>
</p>
<p class="member">
<a href="/mypage/diary/detail/7369">
<img src="http://img01.e-3.ne.jp/diary/10000/80x80/0_1.jpg" class="image_list" alt="" />
</a>
</p>

ただし、80x80というディレクトリにはサムネイル画像があって、640x640というディレクトリに大きい画像があるようだったので、そこは無名関数を使って置換している。その辺の細かい書き方は、Web::Scraper の使い方を調べてほしい。

■ソース

collector.pl
use FindBin;
use lib "$FindBin::Bin/lib";
use strict;
use warnings;
use Web::Collector;
use YAML::Syck;

my $yaml = $ARGV[0] || die "Usage: collector.pl [config.yaml]¥n";
my $c = new Web::Collector($yaml);
$c->run();

Web::Collector.pm
package Web::Collector;

use warnings;
use strict;
use Carp;
use YAML::Syck;
use File::Spec;
use File::Basename;
use LWP::Simple;
use URI;
use Web::Scraper;

use version;
our $VERSION = qv('0.0.1');

use base qw/Class::Accessor::Fast/;
__PACKAGE__->mk_accessors(qw(conf already uri have_files exists skip_cnt max_cnt));

sub new {
my ( $class, $yaml_file ) = @_;
my $conf = LoadFile($yaml_file);
my $already = File::Spec->catfile( $conf->{'img_dir'}, '.already.yml' );
unless ( -e $already ) {
open my $fh, ">", $already;
print $fh "---¥n";
close $fh;
}
my $have_files = LoadFile($already);
$have_files = [] unless ( ref $have_files );
my $self = {
'conf' => $conf,
'already' => $already,
'uri' => $conf->{'uri'},
'have_files' => $have_files,
'exists' => {},
'skip_cnt' => 0,
'max_cnt' => $conf->{'max_cnt'},
};
bless $self, $class;
}

sub run {
my $self = shift;
$self->_init();
foreach ( 1 .. 2 ) {
my $baseuri = $self->uri . $_;
my $uris = $self->get_uris($baseuri);
$self->get_images($uris);
}
$self->_end();
}

sub _init {
my $self = shift;
$self->_cache;
}

sub _end {
my $self = shift;
DumpFile( $self->already, $self->have_files );
}

sub _cache {
my $self = shift;
if ( ref $self->have_files ) {
foreach ( @{ $self->have_files } ) {
$self->exists->{$_}++;
}
}
}

sub get_uris {
my ($self, $baseuri) = @_;
my $uri = URI->new($baseuri);
my $scraper = scraper {
eval $self->conf->{'process'};
};
my $result = $scraper->scrape($uri);
return $result->{img_uri};
}

sub get_images {
my ($self, $uris) = @_;
foreach (@$uris) {
$self->get_image($_);
last if ( $self->skip_cnt >= $self->max_cnt );
}
}

sub get_image {
my ($self, $u) = @_;
$u = URI->new($u) unless (ref $u);
my $basename = basename( $u->path );
my $file = File::Spec->catfile( $self->conf->{'img_dir'}, $basename );
if ( $self->exists->{$basename} ) {
print "Skip: $file";
$self->{skip_cnt}++;
}
else {
print "Get: $file";
getstore( $u, $file );
$self->add_have_file($basename);
}
print "¥n";
}

sub add_have_file {
my ($self, $file) = @_;
push( @{$self->have_files}, $file );
}

1;


ロリポブログのせいなんだけど、これをコピーしても使えないのでソースが欲しい方はcodereposからどうぞ。
http://svn.coderepos.org/share/lang/perl/Web-Collector/trunk/

ページをたどるところは、page= とかで決めうちでたどるのではなく、WWW::Mechanize とかでもっと賢くたどれるようにしたい。
この記事のトラックバックURL
http://en.yummy.stripper.jp/trackback/976179
トラックバック
僕もしゃべる @Yokohama.pm テクニカルトーク #3
■11/28(金) にYokohama.pm #3 が開催されますYokohama.pm テクニカルトーク #3 - Yokohama Perl Mongers Yokohama.pm #3 スケジュール表( ← こっちのが最新情報 ) ボ会仲間のfujikake さんがしゃべるらしいボ会でのZIGOROuさんとのご縁 (というか「技術者は絶対
| ブログが続かないわけ | 2008/11/14 12:22 PM |
コメント
目的はえっちの割り切りえっちサイト
| セフレ | 2009/01/13 10:34 AM |









関連情報