Web::Scraper を HTML::TreeBuilder::LibXML で高速に

昨日、HTML::TreeBuilder::LibXML がリリースされました。
Web::Scraper を 16 倍速くする HTML::TreeBuilder::LibXML を書いた

http://search.cpan.org/~tokuhirom/HTML-TreeBuilder-LibXML-0.01_02/
デベロッパーリリース)


http://search.cpan.org/~tokuhirom/HTML-TreeBuilder-LibXML-0.03/
正式リリースされました。

これまでも、Web::Scraper を XML::LibXML で爆速にする hack!などはあったのですが、 HTML::TreeBuilder::LibXML は Web::Scraper にパッチを当てる必要もなく、使う側で use して replace_original() を呼ぶだけなので入れ替えも楽です。


使い方は、今まで

use URI;
use Web::Scraper;
my $uri = URI->new("http://example.com/");
my $scraper = scraper {
    process 'a', 'link[]' => '@href';
};
my $result = $scraper->scrape($uri);

と、していたところを、

use URI;
use Web::Scraper;
use HTML::TreeBuilder::LibXML;
HTML::TreeBuilder::LibXML->replace_original();
my $uri = URI->new("http://example.com/");
my $scraper = scraper {
    process 'a', 'link[]' => '@href';
};
my $result = $scraper->scrape($uri);

と、2行追加するだけです。



ある程度のデータ量があるページでどれくらい処理速度が違うかベンチマークを取ってみました。

普通に 1000 回とかスクレイピングで回すと DoS攻撃 になってしまうので、

wget -O /tmp/w3.org.html http://www.w3.org/

で、スクレイピング対象をローカルに保存し、

#!/usr/bin/perl
use strict;
use Benchmark;
use Web::Scraper;
use URI;
use HTML::TreeBuilder::LibXML;

my $uri = URI->new("file:///tmp/w3.org.html");
my $scraper = scraper {
    process 'a', 'title[]' => '@title';
};

timethese(1000, {
    'XPath' => sub {
        my $result = $scraper->scrape($uri);
    }
});

HTML::TreeBuilder::LibXML->replace_original();

timethese(1000, {
    'LibXML' => sub {
        my $result = $scraper->scrape($uri);
    }
});

と、しました。
結果は以下です。10倍以上のスピードアップです。

Benchmark: timing 1000 iterations of XPath...
     XPath: 268 wallclock secs (109.53 usr +  0.15 sys = 109.68 CPU) @  9.12/s (n=1000)
Benchmark: timing 1000 iterations of LibXML...
    LibXML: 24 wallclock secs ( 9.80 usr +  0.08 sys =  9.88 CPU) @ 101.21/s (n=1000)