[広告]

2014年7月5日土曜日

WEBスクレイパー開発記録(5) lxmlで試行錯誤

基本方針

とにかくurlwatchでうまくいかなかった部分ができるようになったスクリプトをPythonで書いてしまおうというのが目標です。

  • 文字化けせずに、日本語のWEBサイトを適切に扱えること
  • フィルタ機能があること
  • 再実行を繰り返してフィルタ機能のデバッグができること(ダウンロードした元ファイルが残っていること)

具体的にはこの3点でしょうか。これに加えてパフォーマンスがよければ言うことなしです。

urlwatchのソースを参考に、ファイルをWEBからとってくる部分と、ファイルを比較する部分に分けて考えていくことにしました。

そして、ファイルをWEBからとってくる部分については、ダウンロードした元ファイルを残すという方針があるわけですから、こちらは比較的簡単でした。

urlwatchを見習って、URLをハッシュ化したものをファイル名にすることにしました。

URLそのままですと、ファイル名に使えない文字が入ってしまう可能性があります。ファイル名に使えない文字というのはOSによって異なったりするので、変に置換すると収拾がつかなくなったりしますので、ハッシュ値のほうが取り回しやすいだろうと考えました。

HTMLパーサの採用

次にフィルタ部分をどうするかを考えました。

ありがちなのは、例えば、「あなたは○人目のお客様です」とか「今日のお天気は晴れです」とかいった、更新をチェックする意味はないけれども、毎回のように更新されている部分をHTMLから取り外す処理です。

これは正規表現でやるにはかなり無理があります。

やはり正攻法で、HTMLをパースして、XPath式か、CSSセレクタのようなもので、読み取る範囲や削除する範囲を指定する方向で考えたほうがよいようです。

問題になるのは、動物園のWEBサイトがどれくらい正当なHTMLで記述されているかという部分です。

こればっかりはやってみないと分かりません。

とりあえず、WEBを検索して、2つのパーサが候補になりました。

一つはlxml。とにかく高速なのが売りです。

もう一つはbeautifulsoup。こちらはエラー含みのHTMLの処理が得意です。

安直に考えて、まずはlxmlで試して、どうしてもうまくいかなかったら、例えばそのサイトだけbeautifulsoupで処理すればとても効率がよさそうです。

というわけで、lxmlを使うところから始めたのでした。

lxmlを使っての迷走

lxmlを試すにあたって、まずは札幌市円山動物園のWEBサイトにアクセスしました。北から順、というか、都道府県コードでいくとどうしてもこの動物園がトップにきますので。

ここのWEBサイトはxhtmlでutf-8で書かれています。HTML-lintを通したところ、「53個のエラーがありました。このHTMLは 78点です。」という結果がでました。まずまずの出来ではないでしょうか。

で、これをlxml.htmlでパースすると、とりあえず、無事読み込んでくれているようです。

抽出したい部分をXPath式で指定すると、うまくいきます。

これはいけそうだというので、続いて旭山動物園。こちらはいきなりフレームレイアウトで文字コードはShift_JIS。これだけ人気の動物園でも、WEBサイトはこんなものなのですね。

どうもホームページビルダーを使っているようなのですが、WEBサイト担当の方とか、業務量的に大変ではないでしょうか。

こちらもlxml.htmlでパースしてみると、うまくいっているようです。

lxmlは、文字コードはHTML内に記述された、charsetを見てくれているようです。

と、ここで問題発生です。

出力が短すぎ。どうもHTMLを最後まで読み込んでくれていないらしいことが分かりました。

何もエラーなど出ていなかったはずなのですが。

lxmlの公式サイトを見ても、lxml.htmlの部分については、エラーハンドリングの記述がありません。

エラーがあるなら例外を吐いて止まってくれればいいのに、何も言わずに結果を切り詰めるなんて...

ちなみに、同じHTMLをbeautifulsoupでパースすると、文字化けします。が、これは文字コードを適切に指定すればうまくいきました。どうもbeautifulsoupは、HTML内のcharsetを見てくれていないようです。

文字コードの部分だけ解決すれば、beautifulsoupで最後までパースできているようなのですが、こちらはかなり遅いということが分かりました。

さて、困ったのは

  • lxml.htmlが適切なエラーを出力してくれないので、自動でbeautifulsoupに処理を移す策が立てられない
  • beautifulsoupで処理しようとすると、あらかじめ文字コードが分かっていないといけない

といった2点で、これらについてはとりあえず、手動。すなわち、旭山動物園のサイトを処理する際には、beautifulsoupを使用して、あらかじめShift_JISという文字コードを与えてやるという対応にしたのでした。

それで先に進めるわけですから。

ここから先は同様で、まずlxml.htmlでパースをしてみて、うまくいかないようなら、beautifulsoupで試す、の繰り返しで、それぞれの動物園ごとの設定ファイルを書いていったのでした。

200サイトもあるので、この作業だけで3日くらいかかったのですけれど。