perlのWeb::Scraperみたいな記述で、ページの中からデータを取り出すwebscraper.jsという小さなjavascriptのライブラリを書きました。
webscraper.jsを読み込んでFirebugコンソールで使います。

$ebay_auctionに一度代入されている部分をそのままインラインで書き直すと
my $ebay = scraper {
process "table.ebItemlist tr.single",
"auctions[]" => scraper {
process "h3.ens>a",
description => 'TEXT',
url => '@href';
process "td.ebcPr>span", price => "TEXT";
process "div.ebPicture >a>img", image => '@src';
};
result 'auctions';
};
こうなります。Web::Scraperではページのどの部分を取り出すかの記述をDSLっぽいperlのコードで記述します。javascriptはperlのようにはコードの記述方法に自由さがないのでWeb::Scraperのように要素の記述をjavascriptの式で表現するのはあきらめて、JSONで記述することにしています。上のWeb::Scraperのperlのコード部分をJSONで
WebScraper( {
'table.ebItemlist tr.single': {
'auctions[]': {
'h3.ens>a': {
description: 'TEXT',
url: '@href'
},
'td.ebcPr>span': {
price: "TEXT"
},
'div.ebPicture >a>img': {
image: "@src"
}
}
}
} ).scrape(document);
というふうに表現して書きます。
要素の指定にはWeb::Scraperと同様にCSSセレクタ、もしくはXPathを使います。CSSセレクタでの指定部分はLatest topics > selector.js改 - outsider reflexのselector.jsを使わせていただいています。

Web::Scraperと同じかたちで取り出したデータが入ります。
なぜならCSSセレクタの記述はXPathとしても正しい記述なものが多いからです。たとえばh1の子要素のaという意味の h1 > a というCSSセレクタは、"コンテキストノードの子要素のh1とaの中のテキストを数値に変換したときh1の中身のほうが大きいか"という意味の正しいXPath式として評価できます。そのため、単純にXPath式として正しくないものがCSSセレクタという判別ができません。
Web::Scraperはどうしているのかなーと思ってみたところ
my $xpath = $exp =~ m!^(?:/|id\()! ? $exp : HTML::Selector::XPath::selector_to_xpath($exp);
というコードで、式のはじめが/かidのときはXPathという判別方法が使われていました。
webscraper.jsでは、もうちょっと柔軟にXPathが書けるように次のような方法を採ることにしました。
document.createExpressionでXPath式として評価できるかを調べる。できなかったらCSSセレクタ\.\w+にマッチする文字列がクオートされていない部分にあったらCSSセレクタ[<+\->]にマッチする文字列が [ ] で囲まれていない部分にあったらCSSセレクタdocument.createExpressionにかけたほうが速いだろうという気持ちで入れています。ここでNGなものは確実にXPathじゃないわけですし。
2番目の条件は、CSSセレクタでよく使われる . はAutoPagerize SITEINFOを見るとXPathではファイルの拡張子以外で使われていないので、クオートされていないところで . を使うような式はCSSセレクタとして書いたつもりの式だと判断するために入れました。
3番目の条件も同様にXPathでは <, +, -, > は演算子として [ ] の中に書くことがほとんどなので、[ ]の外側で使っていたらCSSセレクタだと思うことにしました。
トラックバック元エントリにこのエントリへのリンクがない場合はトラックバックを受け付けません。
http://labs.gmo.jp/mt/mt-tb.cgi/201
comments