Web::Scraper Watch

0.21_01 まで。

0.21_01 Thu Oct 4 01:05:00 PDT 2007
  - Added an experimental filter support
   (Thanks to hirose31, tokuhirom and Yappo for brainstorming)
0.21 Wed Oct 3 10:37:13 PDT 2007
  - Bumped up HTML::TreeBuilder dependency to fix 12_html.t issues
   [rt.cpan.org #29733]
0.20 Wed Oct 3 00:28:13 PDT 2007
  - Fixed a bug where URI is not absolutized with a hash reference value
  - Added eg/jp-playstation-store.pl

http://search.cpan.org/src/MIYAGAWA/Web-Scraper-0.21_01/Changes

フィルターが導入された。DEVELOPER RELEASE なので激しく変わる予感。

とりあえず今までどおりのコード。フィルターをいろいろ試したいのでかなり無駄なことします。

#!/usr/bin/perl
use strict;
use warnings;
use utf8;

use Web::Scraper;
use URI;
use YAML;

my $uri = URI->new('http://d.hatena.ne.jp/hetappi/searchdiary?word=未読');
print Dump scraper {
  process '//div[@class="hatena-asin-detail"]/a[@href=~/\d{9}[\dX]/]',
    'values[]' => '@href';
}->scrape($uri);
---
values:
  - !!perl/scalar:URI::http http://d.hatena.ne.jp/asin/4873113296
...
  - !!perl/scalar:URI::http http://d.hatena.ne.jp/asin/4061825550

関数で URL 文字列に変換してみる。

sub to_string {
  $_->as_string;
}
print Dump scraper {
  process '//div[@class="hatena-asin-detail"]/a[@href=~/\d{9}[\dX]/]',
    'values[]' => ['@href', \&to_string];
}->scrape($uri);
---
values:
  - http://d.hatena.ne.jp/asin/4873113296
...
  - http://d.hatena.ne.jp/asin/4061825550

無名関数をその場で渡して ISBN(≒ASIN)だけ取得する。

print Dump scraper {
  process '//div[@class="hatena-asin-detail"]/a[@href=~/\d{9}[\dX]/]',
    'values[]' => ['@href', \&to_string, sub { /(\d{9}[\dX])/, $1 }];
}->scrape($uri);
---
values:
  - 4873113296
...
  - 4061825550

Web::Scraper::Filter のサブクラスを定義して ISBN13 に変換する。

package Web::Scraper::Filter::ISBN13;
use base qw(Web::Scraper::Filter);
use Business::ISBN;

sub filter {
  my ($self, $value) = @_;
  Business::ISBN->new($value)->as_isbn13->as_string([]);
}

1;

...

print Dump scraper {
  process '//div[@class="hatena-asin-detail"]/a[@href=~/\d{9}[\dX]/]',
    'values[]' => ['@href', \&to_string, sub { /(\d{9}[\dX])/, $1 }, 'ISBN13'];
}->scrape($uri);
---
values:
  - 9784873113296
...
  - 9784061825550

独自の名前空間にサブクラスを定義して、ブックオフオンラインの URL を生成する。

package MyFilter::BookOff;
use base qw(Web::Scraper::Filter);

sub filter {
  my ($self, $value) = @_;
  'http://www.bookoffonline.co.jp/feed/search,st=u,q=' . $value;
}

1;

...

print Dump scraper {
  process '//div[@class="hatena-asin-detail"]/a[@href=~/\d{9}[\dX]/]',
    'values[]' =>
      ['@href', \&to_string, sub { /(\d{9}[\dX])/, $1 }, 'ISBN13', '+MyFilter::BookOff'];
}->scrape($uri);
---
values:
  - 'http://www.bookoffonline.co.jp/feed/search,st=u,q=9784873113296'
...
  - 'http://www.bookoffonline.co.jp/feed/search,st=u,q=9784061825550'

これだと簡単すぎて意味ないけど、汎用的にクラスなり関数なりでフィルターを定義しておけば便利ですね。

ただ、おそらく miyagawa さんの本命は Web::Scraper with filters, and thought about Text filtersの後半にあるアイディアなんだろうなー。Plagger などと共通のインタフェースで、オプションを指定できたりするテキストフィルター。めっちゃ期待しています。