何度も取り上げていますが、Ruby製のクローラーであるAnemoneについてです。もう2年ほどメンテナンスされていないものの、Rubyの中のクローラー・フレームワークとしては未だに一番の完成度です。しかし、残念ながら幾つかの問題点があります。その中で日本語を扱う我々にとっては一番大きな問題は、文字化けです。
Anemoneの文字化けの原因
Anemoneの文字化けの原因は、ずばりUTF-8以外の考慮が何もされていないためです。Anemoneが利用するHTMLパーサーであるNokogiriは、もともと内部的な文字コードをUTF-8として扱います。UTF-8以外の文字コードを扱う場合は、文字コードを指定して渡す必要があります。それにもかかわらず、AnemoneがHTMLをパースする時は、次のような実装になっています。
# # Nokogiri document for the HTML body # def doc return @doc if @doc @doc = Nokogiri::HTML(@body) if @body && html? rescue nil end
この実装ではUTF-8やASCII以外の場合は、文字化けが発生します。
小手先のAnemoneの文字化け対策
対策の1つとしては、AnemoneがパースしたNokogiriドキュメントを使わない方法があります。
取得先のページの文字コードをみて、String#encodeを使って明示的に変換します。
require 'anemone' urls = [] urls.push("http://www.amazon.co.jp/gp/bestsellers/books/466282/") Anemone.crawl(urls, :depth_limit => 0) do |anemone| anemone.on_every_page do |page| doc = Nokogiri::HTML.parse(page.body.encode("utf-8","shift_jis")) puts doc.xpath("//title").text end end
実行すると、この通り文字化けせずに結果を取得できます。
$ ruby anemone-convert.rb Amazon.co.jp ベストセラー: ビジネス・経済 の中で最も人気のある商品です
抜本的なAnemoneの文字化け対策
一方で、折角Anemone内部でNokogiriドキュメントを作っているのに、それを切り捨てて新たなNokogoriドキュメントを作るのは無駄以外の何者でもありません。また、複数のサイトをクローリングする際は、決め打ちの文字コードの対処はできません。抜本的な対策としては、Anemone内部の問題を解決するのが良いでしょう。
AnemoneがNokogiriドキュメントを生成しているのは、Anemone::Pageクラスのdocメソッドで実体はlib/anemone/paage.rbにあります。問題は、変換の際の文字コードに何を指定すれば良いかという点です。Anemoneが扱うのは、HTMLです。なので、HTML中からcharsetを抜き出し、それをセットすることにしましょう。幸い、Anemoneにはcontent_typeというメソッドがあり、HTMLヘッダーからcontent-typeを抜き出してくれています。ここからcharsetを抜き出せば大丈夫です。
# # Nokogiri document for the HTML body # def doc return @doc if @doc if @body && html? if charset == 'utf-8' || charset.nil? body = @body else body = @body.encode("UTF-8", charset, :invalid => :replace, :undef => :replace) rescue nil end @doc = Nokogiri::HTML(body) if body end end # # The content-type returned by the HTTP request for this page # def content_type headers['content-type'].first end # # The charset returned by the content-type request for this page # def charset matcher = content_type.match(/charset=[\"]?([a-zA-Z\_\-\d]*)[\"]?/) matcher[1].downcase if matcher end
実装している途中で気がついたのですが、anemoneのPull Requestの中にほぼ同じ実装がありました。totothinkという方によるForkです。
totothink/anemone · GitHub
私の方では、少し実装を変えています。content_typeのマッチの部分が「_」が含まれていなかった為にShift_JISに対応出来かなったので追加しています。また、もともとのHTMLのbodyが格納されている@body変数も、変換することなく元のままで残しております。その方が、後々使いやすいと考えたからです。
追記 2014/04/07
content_typeだけで大丈夫なのというご指摘ありました。その通りだと思います。が、必要に応じて、変更していけば良いと思います。(MetaタグとかLangとか優先順位をつけて判定)ちなみに@bodyに入っている文字列をString#encodingで判定しようとしたのですが、必ずUS_ASCIIと判定されるので使えません。
Fork版のGitHubからのインストール
GitHubにForkして、修正版を置いています。興味あれば使ってください。
git clone https://github.com/takuros/anemone.git cd anemone gem build anemone.gemspec gem install anemone-0.7.2.gem
まとめ
Pythonには、ScrapyというCrawlerのフレームワークがあります。今でも開発は活発です。RubyのCrawlerフレームワークとして網羅的な機能を持つのはAnemoneしかありません。Anemoneは残念ながら、もう開発されていない模様です。Pull Requestも幾つもあるけど、取り込まれる様子はありません。Forkで何か新しいの作っても良いのかもしれませんね。
See Also:
あらためてRuby製のクローラー、"anemone"を調べてみた
オープンソースのRubyのWebクローラー"Anemone"を使ってみる
JavaScriptにも対応出来るruby製のクローラー、Masqueを試してみる
複数並行可能なRubyのクローラー、「cosmicrawler」を試してみた
takuros/anemone · GitHub
参照:
chriskite/anemone · GitHub
anemone RDoc
- 作者: るびきち,佐々木拓郎
- 出版社/メーカー: SBクリエイティブ
- 発売日: 2014/08/25
- メディア: 大型本
- この商品を含むブログ (1件) を見る
Spidering hacks―ウェブ情報ラクラク取得テクニック101選
- 作者: Kevin Hemenway,Tara Calishain,村上雅章
- 出版社/メーカー: オライリー・ジャパン
- 発売日: 2004/05
- メディア: 単行本
- 購入: 52人 クリック: 904回
- この商品を含むブログ (103件) を見る