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 でも便利になる気が!