ブログが続かないわけ

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

Web::Scraper 使い方(超入門)

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

3年前にこの本に出会って、僕はスクレーピングの虜になった。

おかげで正規表現をたくさん書く機会が得られたし、CPANのモジュールともたくさん触れ合うことができたし、さらに自分自身でも構造化されたHTMLを書く習慣も身についた。 ただ、やっぱり、スクレーピングは簡単ではない部分もたくさんあると思う。文字コードについても意識しなければならないし、なにより正規表現で必要な部分を抽出することそのものが結構骨のいる作業だ。HTML::TreeBuilder を使った方がいいのか、それとも正規表現だけでやったほうが楽なのか。そんなことを考慮しながらコーディングして行く。そこがスクレーピングの楽しいところでもあるのだが、そこがスクレーピングの敷居を高いものにしていることも否めない。

そんな、愛らしくもあり憎たらしくもあるスクレーピングを簡単にやってしまうのがこのモジュール、Web::Scraper だ。 今回はこのWeb::Scraper を簡単に使うための初歩の初歩を、Web::ScaperのPodドキュメントを補う形で自分の理解をまとめた。

Web::Scraper を使ってのスクレーピングの流れはとても簡単だ。

  1. どんな条件で抽出するかを記述したscraper(コードリファレンス?)を書く。
  2. それを使って、scrape に対象となるURLを渡して実行する。

my $scraper = scraper { [抽出条件] };
$scraper->scrape( URI->new([対象サイトURL]) );

こんなイメージ。

で、早速、具体例。具体例はPodのSynopsis。 (赤字部分は便宜上追加したところ)

Web::Scraper Synopsysより
  use URI;
  use Web::Scraper;
  use YAML::Syck;

  my $ebay_auction = scraper {
      process "h3.ens>a",
          description => 'TEXT',
          url => '@href';
      process "td.ebcPr>span", price => "TEXT";
      process "div.ebPicture >a>img", image => '@src';

      result 'description', 'url', 'price', 'image';
  };

  my $ebay = scraper {
      process "table.ebItemlist tr.single",
          "auctions[]" => $ebay_auction;
      result 'auctions';
  };

  my $res = $ebay->scrape( URI->new("http://search.ebay.com/apple-ipod-nano_W0QQssPageNameZWLRS") );
  print Dump($res);

紹介されているSynopsis では何が取得できるのかわからないので、ちょっとだけコードを追加して、取得した情報を画面に表示できるようにした。 (ここでは結果をDumpするのにYAML::Syck を使っているが、もちろんData::Dumperなどで代用もできる。)

(参考)取得結果

〜前略〜
- 
  description: Stereo Earphone Headphone For Apple iPod nano Video W
  image: http://thumbs.ebaystatic.com/pict/1701110335328080_1.jpg
  price: $2.46
  url: http://cgi.ebay.com/Stereo-Earphone-Headphone-For-Apple-iPod-nano-Video-W_W0QQitemZ170111033532QQihZ007QQcategoryZ111693QQrdZ1QQcmdZViewItem
- 
  description: For iPod Nano 1st G Apple iPod Battery+Replacement Kit
  image: ~
  price: $4.98
  url: http://cgi.ebay.com/For-iPod-Nano-1st-G-Apple-iPod-Battery-Replacement-Kit_W0QQitemZ250113923030QQihZ015QQcategoryZ118261QQrdZ1QQcmdZViewItem
- 
  description: For iPod Nano 1st 2nd Gen Sport Arm Band ~ Apple Green
  image: ~
  price: $2.98
  url: http://cgi.ebay.com/For-iPod-Nano-1st-2nd-Gen-Sport-Arm-Band-Apple-Green_W0QQitemZ250113923033QQihZ015QQcategoryZ56170QQrdZ1QQcmdZViewItem
〜後略〜

このSynopsisはシンプルだけれども、コールバックが使えることも示唆していて、素晴らしいサンプルだと思う。 このコードがどういうことをやっているのか、上から見てみた。

      process "h3.ens>a",
          description => 'TEXT',
          url => '@href';

これは 「class属性がensで始まる<h3>エレメントの内側にある<a>エレメントについて」、
 「TEXTノードをdescriptionというkeyのハッシュに」、
 「href属性をurlというkeyのハッシュにいれなさい」、
という意味だ。あとはもうほとんど一緒。
 「class属性がebcPrで始まる<td>エレメントの内側にある<span>エレメントについて」、
 「TEXTノードをpriceというkeyのハッシュにいれなさい」
続いて、
 「class属性がebPictureで始まる<div>エレメントの内側にある<a>エレメントの内側にある<img>エレメントについて」、
 「src属性をimgというkeyのハッシュにいれなさい」
で、最後にそれを結果として返すようにするだけ。

      result 'description', 'url', 'price', 'image';

実際に、http://search.ebay.com/apple-ipod-nano_W0QQssPageNameZWLRSにアクセスして、そのHTMLのソースと見比べるとよくわかる(HTMLの掲載は割愛)。

このサンプルコードには続きがあって、もう1つのscraper が定義されている。 実際にscrape が実行されているのはこちらのようだ。 この部分が、scraper にコールバック関数を使えることを示している。

      process "table.ebItemlist tr.single",
          "auctions[]" => $ebay_auction;
      result 'auctions';

この部分はこういう風に解釈できる。 「class属性がebItemlistで始まる<table>エレメントの内側にある、class属性がsingleで始まる<tr>エレメントについて」、  「$ebay_auction というコードリファレンスで指定されるメソッドを実行して、その結果をauctions という配列にいれなさい」 いまだに、テーブルコーディングされているサイトも多いし、div でCSSコーディングされているサイトであっても、ブロックが入れ子構造になっているサイトは多いと思う。 そういうサイトからデータを抽出するときは、まず、どのブロックからデータを取得するのかを大まかに指定してしまって、その中で細かい情報を取得するという段階を踏むと、スクレーピングしやすい。このSynopsis でコールバックを使っているのは、まさにそういう方法を見せるためだと思う。

まず、どのブロックからデータを取得するのか。ここでは、<table class="ebItemlist">の内側からデータを取るよ、ということを$ebay で指定して、実際にそのブロックの中のどの部分をどのように抽出するのかという細かいところを$ebay_auction で指定しているのだろう。

このコードの結果と、このサイトのHTMLソースを見比べるとよくわかった。 また、実際に試しているひともたくさんいて、その人たちの解説や素敵なサンプルもためになった。

この記事のトラックバックURL
http://en.yummy.stripper.jp/trackback/555416
トラックバック
Web::ScraperでCISCO RECORDSをスクレーピング
いまさらWeb::Scraperを使ってみました。 使うネタがなかったのがスルーし続けた一番の理由だったのですが、それではいけないと思い、CISCO RECORDSの商品情報を取得するスクリプトという...
| hide-k.net#blog | 2007/09/05 2:33 PM |
Web::ScraperでジャグラBBをスクレーピング
Web::ScraperでジャグラBBのページをスクレーピングしたよ。スゲエ便利だね! ジャグラBB - 印刷業のためのWebラーニングサイト:HOME [www.jagra.or.jp] script:jagrabb.pl #!/usr/bin/perl use strict; use warnings; use Web::Scraper; use URI; my $uri = 'http://
| M.C.P.C. | 2007/09/06 4:41 PM |
ニコニコ動画をWeb::Scraperでスクレイプ
ニコニコ動画のタグごとにRSSを作るため、最初は以下のように正規表現でゴリゴリと処理していたが、イマイチ綺麗じゃない。while($html =~ m{<p><a\s+href="watch/(\w+)">\s* ...
| Magical Diary, beta version | 2007/10/06 9:42 PM |
Web::Scraper でいい感じのデータ構造になってくれなくて困っているのはどこのどいつだ〜? アタイだよ!
例えばこんなHTMLからニュースの一覧を取得することを考えよう。 &lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot; &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;&gt; &lt;html la
| ブログが続かないわけ | 2008/01/16 7:43 PM |
Amazon アフィリエイトのレポートをメールで自動で受けとる 2011年版
Amazon のアフィリエイトのレポートは毎回ログインしないと見れないのがわずらわしいです。僕のブログなんてどうせ、という気分で頑張ってログインすると、だいたい、ほんとになにもありません。デイリーレポートなんて存在意義すらわからないです。レポート対象期間を
| ブログが続かないわけ | 2011/02/25 12:07 PM |
コメント
丁寧な解説をありがとうございます。
二段階あるのは繰り返しがあるからではないでしょうか?
| aoi | 2008/01/10 5:07 PM |
あ!
そうかもしれない。

コールバック関数を使って2段階にするのが面倒で手を抜くと、
求めていたようなデータ構造が取得できないことがよくあったけど、
そのせいだ。間違いない。

ご指摘ありがとうございます。

今度、それをもう少し説明しようと思います。
| junichiro | 2008/01/10 9:36 PM |
2段階取得について、新しいエントリでまとめました。
よかったらご覧ください。

http://en.yummy.stripper.jp/?eid=800109
| junichiro | 2008/01/16 7:53 PM |









関連情報