Help us understand the problem. What is going on with this article?

Nokogiriで文字化けを防ぐ

More than 5 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でないページを取得するときの注意

foloinfo
foloinfo / CEO at byus&co.ltd // Vimmer, 最近はめっきりReact
https://drip.ink
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした