Nokogiriでゴリゴリやってます。やっと使い方分かってきました。
Nokogiriだとデフォルトでも割と文字化けしないなと思ったんですがさすがに色んなサイトを対象にしようとすると化けました。ので何とかします。
他にいい方法があったら是非教えて下さい。
###(注意:下に追記があります。binaryで読み込んでkconvのtoutf8、charsetにutf-8を指定でほぼ起きなくなりました。)
require 'open-uri'
require 'nokogiri'
uri = "http://www…"
page = URI.parse(uri).read
charset = page.charset
if charset == "iso-8859-1"
charset = page.scan(/charset="?([^\s"]*)/i).first.join
end
document = Nokogiri::HTML(page, uri, charset)
という形にしています。
open-uriの.charset
で大抵上手に設定してくれるんですが、charsetがiso-8859-1になって化けてしまう場合があります。
その場合はページ内のmetaTag記載に期待して.first
としましたが、もちろんページによっては初めに出てくるcharsetがページ全体のエンコーディングとは別になっている場合もあると思います。
参考にした2011-02-15 Nokogiriが文字化けするのの対策では
if charset == "iso-8859-1"
charset = io.scan(/charset="?([^\s"]*)/i).flatten.inject(Hash.new{0}){|a, b|
a[b]+=1
a
}.to_a.sort_by{|a|
a[1]
}.reverse.first[0]
end
として、ページ内で一番使われているエンコーディングを指定していました。が、これだと僕の対象としていたページでは別のエンコーディング指定が多発(JavaScript用の指定)していた為、上手く行かず.first
を指定することにしました。
他にいい方法をご存知のかたは是非コメントお願いします。
###追記
文字化けの原因はcharsetが未指定だったからではなく、parseしたURIオブジェクトをNokogiriに読み込ませていたからの様です。
page = URI.parse(uri).read
...
document = Nokogiri::HTML(page, uri, charset)
の部分ですね。以下のように変更したところ今のところ文字化けもエラーも起きていません。
# parseされていないhtmlを読みこませる
html = open(url).read
doc = Nokogiri::HTML(html, url)
ローカルのirbやpryではURIオブジェクトから開いたファイルとcharset指定で動いていたのですが、Rails環境で同じ事をした所、特定のサイト上でheadまでしか読み込まれない状態になり、charsetを消すと直るという現象に遭遇しました。解決のためにリサーチしていた所
# Request the HTML before parsing
html = open("http://www.walmart.com.br/").read
# Replace original DOCTYPE with a valid DOCTYPE
html = html.sub(/^<!DOCTYPE html(.*)$/, '<!DOCTYPE html>')
# Parse
doc = Nokogiri::HTML(html)
を見つけ、Parseする前のプレーンなhtmlをNokogiriに投げるべきだと気付き解決しました。
(因みに上のスニペットの解説では、DOCTYPEの指定が不適切なサイトの場合Xpath等を使った指定がおかしくなるため、それを解決するにはDOCTYPEを置き換えればいい、と言うものでした。余談ですが勉強になりました。)
Xpathでの指定が上手く行かず目的のデータを集めるのに手こずっていたのですが、適切な読み込ませ方をすることで快適に使えるようになりました。(今のところは)めでたしめでたし
参考:nokogiri doc.xpath('head') returns nil
追記2
やっぱ文字化けしました。基本的には大丈夫なんだけど特殊な文字含んでたり上手くcharsetを自動で読めなかったりすると化けます。今のところ
require 'kconv'
...
Nokogiri::HTML(html.toutf8, nil, 'utf-8')
で対応してます。
参考:
解決済】RubyのNokogiriはHTMLの日本語エンコーディングを保持しないので困る++
CP932など特殊な文字を含むHTMLをスクレイピングする
###追記3
文字化け。ガッデム!
Encoding::UndefinedConversionError in PostsController#create
"\xAD\xB5" from EUC-JP to UTF-8
という具合に文字化けを含んだ文字列を含んでいるとエラーが出る。
解決案としてバイナリで読み込んで対応した。
html = open(url, "r:binary").read
doc = Nokogiri::HTML(html.toutf8, nil, 'utf-8')