#はじめに
open-uriでhtmlを読み込み、Nokogiriでパースしてスクレイピングを行っていたのですが、その際Nokogiriのエンコーディングをnil
に(もしくは省略)しても文字化けを起こすことなくパースができていました。
open-uriでhtmlを読むときはバイナリで読み込まれるので、バイナリで読み込まれたhtmlを、エンコーディングnil
にしてNokogiriでパースするとどうなるか、ということに焦点をしぼって検証してみました。
#HTML.parseのエンコーディングを「nil」にしてみる
Nokogiriでhtmlをパースするときのエンコーディングをnil
としてみます。HTML.parseの第三引数にnil
を設定します。
今回読み込むのは以下のhtmlです。ファイルはShift_JISで書かれています。
<html>
<head>
<title>こんにちわ</title>
<meta charset="Shift_JIS">
</head>
<body>
</body>
</html>
バイナリモードでhtmlを読み込みます。openメソッドに'rb'
オプションを追記することでバイナリでファイルを読むことができます。
検証のため、バイナリモードで読み込んだ時点での文字コードと、Nokogiriでパースしたあとの文字コードを表示してみます。
require 'nokogiri'
html = open('hello.html', 'rb').read
p html.encoding
p Nokogiri::HTML.parse(html, nil, nil).encoding
実行結果
$ ruby sample.rb
#<Encoding:ASCII-8BIT>
"Shift_JIS"
結果からは、読み込んだhtml自体のエンコーディングはASCII-8BITですが、Nokogiriによってパースされたあとのエンコーディングは元ファイルと同じShift_JISとなっていることが確認できます。
ちなみにHTML.parse(html)
として引数を省略した場合でも、上記と同じ結果が得られます。
#Nokogiriはどこで文字コードを参照しているか
さきほどの検証結果を見るに、Nokogiriはどこかの文字コードを自分で参照しに行っています。どこを参照しているのか。
実は元のhtmlファイルのmeta要素を参照しに行っています。
<meta charset="Shift_JIS">
のcharsetを参照しているというわけです。
試しにcharset部分をUTF-8に変えて、先ほどと同じように文字コードを出力してみます。
<html>
<head>
<title>こんにちわ</title>
<meta charset="UTF-8">
</head>
<body>
</body>
</html>
実行結果
$ ruby sample.rb
#<Encoding:ASCII-8BIT>
"UTF-8"
パース後の文字コードがUTF-8に変わっているのが確認できます。
ちなみにcharsetを無くしてみると、、、
<html>
<head>
<title>こんにちわ</title>
<meta>
</head>
<body>
</body>
</html>
$ ruby sample.rb
#<Encoding:ASCII-8BIT>
nil
パース後の文字コードがnilとなってしまいました。
もちろんこの状態でタイトル等を表示すると文字化けを起こします。
#まとめ
- htmlをバイナリで読み込み、かつNokogiriのエンコーディングを
nil
にすると、Nokogiriは自分で文字コードを参照しに行く - Nokogiriが参照しに行くのは、バイナリで読み込んだhtmlのmeta要素のcharset
- charsetが書かれていないと、Nokogiriのエンコーディングは
nil
となってしまう