Hpricot からテキストを取り出す

scrAPIよりも使いやすい感じのHpricotですが、「innerText」が上手くHTMLエンティティーを戻してくれないので、違うメソッドをつけてみました。

require "rubygems"
require 'hpricot'

class Hpricot::Elem
  def [](a)
    CGI.unescapeHTML(get_attribute(a))
  end
  def to_text
    r = []
    traverse_text{|text|
      case text
      when Hpricot::CData
        r << text.content
      else
        r << CGI.unescapeHTML(text.inner_text.gsub("\n"," ").gsub(/  +/," ").strip)
      end
    }
    r.join
  end
end
hp = Hpricot('<html><boge href="hoge&neko">test& test &amp;  test<![CDATA[ hoge <&amp;> hoge ]]></boge>')

hp.root.inner_text #オリジナル
# => "test& test &amp;  test hoge <&amp;> hoge "
hp.root.to_text
# => "test& test & test hoge <&amp;> hoge "

hp.root.at("boge").get_attribute(:href) #オリジナル
# => "hoge&amp;neko"
hp.root.at("boge")[:href]
# => "hoge&neko"

Hpricotはcssセレクタもつかえるよ!

知らない人も多いようだけど

doc = Hpricot(open("http://d.hatena.ne.jp/keyword/%BA%B0%CC%EE%A4%A2%A4%B5%C8%FE"))
doc.at("span.furigana").to_text
# => こんのあさみ
doc.at("span.title > a:first-child").to_text
# => 紺野あさ美
doc.at("ul.list-circle > li:first-child > a").to_text
# => アイドル

(サンプルは 川o・-・)<2nd life - ruby のスクレイピングツールキット scrAPI をインスパイア)

t*追記:

CGI.unescapeHTML が、そのままだと 「& &amp;」とか上手く元に戻せないので、http://d.hatena.ne.jp/walf443/20070204/1170605669 の修正をするといいよ!