Ruby
Nokogiri

Nokogiriで文字化けを防ぐ

More than 3 years have passed since last update.

Nokogiriでゴリゴリやってます。やっと使い方分かってきました。

Nokogiriだとデフォルトでも割と文字化けしないなと思ったんですがさすがに色んなサイトを対象にしようとすると化けました。ので何とかします。

他にいい方法があったら是非教えて下さい。


(注意:下に追記があります。binaryで読み込んでkconvのtoutf8、charsetにutf-8を指定でほぼ起きなくなりました。)


Sample

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が文字化けするのの対策では


charset

  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に読み込ませていたからの様です。


Sample_before

page = URI.parse(uri).read

...
document = Nokogiri::HTML(page, uri, charset)

の部分ですね。以下のように変更したところ今のところ文字化けもエラーも起きていません。


Sample_after

# parseされていないhtmlを読みこませる

html = open(url).read
doc = Nokogiri::HTML(html, url)

ローカルのirbやpryではURIオブジェクトから開いたファイルとcharset指定で動いていたのですが、Rails環境で同じ事をした所、特定のサイト上でheadまでしか読み込まれない状態になり、charsetを消すと直るという現象に遭遇しました。解決のためにリサーチしていた所


snippet_form_stackoverflow

# 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を自動で読めなかったりすると化けます。今のところ


sample

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

という具合に文字化けを含んだ文字列を含んでいるとエラーが出る。

解決案としてバイナリで読み込んで対応した。


sample

    html = open(url, "r:binary").read

doc = Nokogiri::HTML(html.toutf8, nil, 'utf-8')

参考:

open-uriでUTF-8でないページを取得するときの注意