ruby のスクレイピングツールキット scrAPI

http://blog.labnotes.org/category/scrapi/
rubyスクレイピングして web の情報を取得するのには、今まで正規表現xpath でやってたので、わりと面倒でした。で、ふと scrAPI というスクレイピングツールキットを知ったのですが、これがかなり便利そう。
このツールキットを使うと、CSS3 なセレクタを記述することで、要素を取得することができます。たとえばとあるサイトのリンクを全部取得したければ、

require 'rubygems'
require 'scrapi'
require 'open-uri'
require 'nkf'
require 'pp'

$KCODE = 'u'

links = Scraper.define do
  process "a[href]", "urls[]"=>"@href"
  result :urls
end.scrape(URI.parse('http://www.hatena.ne.jp/')).sort.uniq

pp links

な感じですっきり記述することができます。
たとえばはてなダイアリーキーワードページから、タイトル、ふりがな、URL、カテゴリを取得したければこんな感じなコードで。

keyword_scrapper = Scraper.define do
  process 'span.title > a:first-child', :title => :text, :url => '@href'
  process 'span.furigana', :furigana => :text
  process 'ul.list-circle > li:first-child > a', :category => :text
  result :title, :furigana, :url, :category
end

html = NKF::nkf('-w -m0', open('http://d.hatena.ne.jp/keyword/%BA%B0%CC%EE%A4%A2%A4%B5%C8%FE').read)
pp keyword_scrapper.scrape(html, :parser => :html_parser)

結果は構造体として取得できるのでアクセスも楽々です。

#<struct
 title="紺野あさ美",
 furigana="こんのあさみ",
 url="/keyword/%ba%b0%cc%ee%a4%a2%a4%b5%c8%fe?kid=800",
 category="アイドル">

一つはまったのが、デフォルトの html の構文解析では Tidy のライブラリを使うのですが、このライブラリがマルチバイト文字列をエンティティにしちゃうので困るという罠が…。そのため、scrape のオプションで pure ruby の HTMLParser を指定することで回避できます。

追記

HTMLParser を指定せずとも、tidy のオプションで

scrape(html, :parser_options => {:char_encoding=>'utf8'})

と渡せばよしなにはからってくれます。コメントで教えてもらいました。


また様々な方法で Scraper を定義できるのでいろいろできそう。海外じゃやたら流行ってる rubyメタプログラミングを多用していてクラス定義などなどが行えます。

今まではスクレイピングには断然 perl のほうが便利だったのですが、この scrAPI を使いこなすだけで、だいぶ ruby でも便利になる気が!