QA@IT

NokogiriでNodeSetに日本語を代入すると文字化けしてしまいます。

3433 PV

以下のようなコードがあるとします。

# -*- coding: utf-8 -*-
require 'nokogiri'
html = <<EOF
<p>
  hello
</p>
<p>
  ストーリー
</p>
EOF
doc = Nokogiri::HTML(html, nil, 'utf-8')

これは正確に日本語を出力してくれます。

しかし中のNodeSetを置き換えようとすると文字化けが起きてしまいます。

doc.css('p').each do |d|
  p d.inner_html
  d.inner_html = 'new'
  p d.inner_html
  d.inner_html = 'ニュー'
  p d.inner_html
end
puts doc

こんな感じです。

[Documents]$ ruby unicode_mojibake.rb 
"\n  hello\n"
"new"
"ã\u0083\u008Bã\u0083¥ã\u0083¼"
"\n  ストーリー\n"
"new"
"ã\u0083\u008Bã\u0083¥ã\u0083¼"
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>ãã¥ã¼</p>
<p>ãã¥ã¼</p>
</body></html>

回答

Nokogiri::HTML::fragmentを使うのはどうでしょうか?

例題ですがNokogiri::HTMLではなくNokogiri::XMLを使うと文字化けせずに出力してくれます。
たぶんNokogiri::HTMLがparseする時になにか問題が起きるようです。Nokogiri::HTML::fragmentはparseせずに文字列をそのまま渡してくれるので大丈夫なようです。

# -*- coding: UTF-8 -*-
require 'nokogiri'
html = <<EOF
<p>
  hello
</p>
<p>
  ストーリー
</p>
EOF
doc = Nokogiri::HTML(html, nil, 'utf-8')


doc.css('p').each do |d|
  p d.inner_html
  d.inner_html = 'new'
  p d.inner_html
  p d.inner_html =  Nokogiri::HTML::fragment('ニュー')
  p d.inner_html
end

puts doc

出力結果

[Documents]$ ruby unicode_mojibake.rb 
"\n  hello\n"
"new"
#<Nokogiri::HTML::DocumentFragment:0x3fcb8a0ef958 name="#document-fragment">
"ニュー"
"\n  ストーリー\n"
"new"
#<Nokogiri::HTML::DocumentFragment:0x3fcb8a0ed888 name="#document-fragment">
"ニュー"
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>ニュー</p>
<p>ニュー</p>
</body></html>

編集 履歴 (0)
ウォッチ

この質問への回答やコメントをメールでお知らせします。