Mojo::DOMとWeb::Scraper
すごい適当だけど、手元のWin32マシン(XP @2.40GHz)でこんな感じ。WWW::Mixi::Scraperとか、Mojoliciousベースで書き換えてもいいかもなーとか思った(しないけど)。ちなみにテキスト抽出時の空白文字の扱いがMojo::DOMとWeb::Scraperで微妙に違うので、$test = 1にするとテストがひとつこけます。あと、抽出するものが単純なら正規表現やらなにやらでごりっと抜いた方が速いはず。
追記:Craftworksさんからブクマでご指摘いただいたように、Web::Scraper::LibXMLを使えば桁違いに速いですね。すっっかり忘れていました。LibXMLありなし版のベンチを同時にとるのは面倒だったので、::LibXMLに差し替えた場合の結果も追記しておきました。ありがとうございます。>Craftworksさん
cf. http://www.perlmonks.org/?node_id=926959
Mojo::DOMと素のWeb::Scraperの場合
perl: v5.12.3 Mojolicious: 3.05 Web::Scraper: 0.36 HTML::Selector::XPath: 0.12 Rate webscraper2 webscraper mojo webscraper2 9.29/s -- -1% -40% webscraper 9.40/s 1% -- -40% mojo 15.5/s 67% 65% -- Rate webscraper mojo webscraper 7.62/s -- -43% mojo 13.4/s 76% -- Rate webscraper mojo webscraper 8.53/s -- -49% mojo 16.8/s 97% --
Mojo::DOMとWeb::Scraper::LibXMLの場合
perl: v5.12.3 Mojolicious: 3.05 Web::Scraper: 0.36 HTML::Selector::XPath: 0.12 XML::LibXML: 2.0002 Rate mojo webscraper webscraper2 mojo 15.6/s -- -76% -80% webscraper 64.6/s 315% -- -15% webscraper2 76.2/s 389% 18% -- Rate mojo webscraper mojo 13.3/s -- -89% webscraper 118/s 794% -- Rate mojo webscraper mojo 15.8/s -- -86% webscraper 114/s 622% --
use strict; use warnings; use 5.010; use Path::Extended; use Web::Scraper; use Mojolicious; use Mojo::UserAgent; use Mojo::DOM; use Data::Dump qw/dump/; use Benchmark qw/cmpthese/; use Test::More; use Test::Differences; say "perl: $^V"; for (qw/Mojolicious Web::Scraper HTML::Selector::XPath/) { say "$_: " . $_->VERSION; } my $test = 0; for (qw( www.yahoo.co.jp )) { my $file = file($_.'.html'); my $html; if ($file->exists) { $html = $file->slurp; } else { my $ua = Mojo::UserAgent->new; # pretend to be IE 8 $ua->name('Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.04506.648)'); $html = $ua->get("http://$_/")->res->body; $file->save($html); } if ($test) { ok get_links_mojo($html); ok get_topics_mojo($html); ok get_shortcut_mojo($html); eq_or_diff [get_links_mojo($html)] => [get_links_scraper($html)]; eq_or_diff [get_topics_mojo($html)] => [get_topics_scraper($html)]; eq_or_diff [get_shortcut_mojo($html)] => [get_shortcut_scraper($html)]; } { my $scraper = scraper { process 'a' => 'links[]' => '@href' }; cmpthese(100, { mojo => sub { get_links_mojo($html) }, scraper => sub { get_links_scraper($html) }, scraper2 => sub { get_links_scraper2($html, $scraper) }, }); } { cmpthese(100, { mojo => sub { get_topics_mojo($html) }, scraper => sub { get_topics_scraper($html) }, }); } { cmpthese(100, { mojo => sub { get_shortcut_mojo($html) }, scraper => sub { get_shortcut_scraper($html) }, }); } } done_testing if $test; sub get_links_mojo { my $html = shift; my $dom = Mojo::DOM->new($html); @{ $dom->find('a')->map(sub { shift->{href} }) }; } sub get_links_scraper { my $html = shift; my $scraper = scraper { process 'a' => 'links[]' => '@href' }; @{ $scraper->scrape($html)->{links} }; } sub get_links_scraper2 { my ($html, $scraper) = @_; @{ $scraper->scrape($html)->{links} || [] }; } sub get_topics_mojo { my $html = shift; my $dom = Mojo::DOM->new($html); @{ $dom->find('div#topicsfb ul.emphasis li')->map(sub { shift->all_text(0) }) }; } sub get_topics_scraper { my $html = shift; my $scraper = scraper { process 'div#topicsfb ul.emphasis li' => 'topics[]' => 'TEXT' }; @{ $scraper->scrape($html)->{topics} || [] }; } sub get_shortcut_mojo { my $html = shift; my $dom = Mojo::DOM->new($html); $dom->at('ul.shortcut li a[href="r/pnp"]')->text; } sub get_shortcut_scraper { my $html = shift; my $scraper = scraper { process 'ul.shortcut li a[href="r/pnp"]' => pnp => 'TEXT'; result 'pnp' }; $scraper->scrape($html); }