Mechanize + Nokogiri の非JIS文字対応

id:otn:20090429 と id:otn:20090630 と id:otn:20090823 の続き。

一旦解決したかに思った、MechanizeのUTF8以外のページの処理ですが、昨日、「mixiの各コミュニティーの最新トピック一覧作成」処理がこけたので調べてみると、№ (1文字の"No.")の文字の所でページが終わったかのごとく処理されてました。

やっぱり、「JIS範囲外の文字が混じったEUC-JPのページ」がうまく処理されないようです。「そもそもそんなものは存在してはいけない」という突っ込みは、mixiの方にお願いします。


ですが、nkfだとちゃんと 1文字の"No." とか 丸付き数字 もEUC-JPからUTF-8に変換できます。ページの文字コード変換はMechanizeじゃなくてNokogiriがしていますが、iconv を使っている。iconvで 丸付き数字 を変換して見ると確かに出来ない。

$ echo 丸付きの1 | nkf -e | iconv -t utf-8 -f euc-jp
iconv: 位置 0 で不正な入力シーケンスがありました


Nokogiriでiconvを使っている部分は、RubyじゃなくてCで書かれているようで手を出せないので、結局、パーサーを差し替えるのが良さそう。最初からこうしていれば良かったのに。mixi用だとこんな感じで。

#! ruby -Ku
# mixi処理
require "rubygems"
require "mechanize"

class MyParser
  def self.parse(thing, url = nil, encoding = nil,
    options = Nokogiri::XML::ParseOptions::DEFAULT_HTML, &block)
#   EUC-JPからUTF-8へ変換を決め打ち。head/metaのContent-Typeのcharset文字列もsubで変換
    thing=NKF.nkf("-wm0E",thing).sub(/euc-jp/,"utf-8")
    Nokogiri::HTML::Document.parse(thing, url, encoding, options, &block)
  end
end

agent=WWW::Mechanize.new
agent.max_history = 1
agent.user_agent_alias = "Windows IE 7"
agent.html_parser = MyParser

agent.get("http://mixi.jp/").form_with(:name=>"login_form") do |form|
  form.set_fields(:email=>MAIL,:password=>PASS)
  form.submit
end

2009-11-25 追記:

Hookでも出来た。最初、出来なかったのはhead/metaのContent-Typeを書き換えてなかったため。ヘッダがあるので、つい、見過ごしていた。

#! ruby -Ku
# mixi処理
require "rubygems"
require "mechanize"

agent=WWW::Mechanize.new
agent.max_history = 1
agent.user_agent_alias = "Windows IE 7"
agent.post_connect_hooks << Proc.new do |params|
  if %r|text/html| =~ params[:response]["Content-Type"]
#   EUC-JPからUTF-8へ変換を決め打ち。head/metaのContent-Typeのcharset文字列もsubで変換
    params[:response_body] = NKF.nkf("-wm0E",params[:response_body]).sub(/euc-jp/,"utf-8")
    params[:response]["Content-Type"]="text/html; charset=utf-8"
  end
end

agent.get("http://mixi.jp/").form_with(:name=>"login_form") do |form|
  form.set_fields(:email=>MAIL,:password=>PASS)
  form.submit
end


どっちがいいかは、難しいところか。

2009-11-26追記:

ふと思いついて、「Shift_JISに対するCP932のようなものがEUC-JPに無いか?」と思って、iconv -l してみる。と、それらしいのがあった。

$ echo 丸付きの1|nkf -e|iconv -t utf-8 -f euc-jp-ms
丸付きの1


ということで、Linuxの場合、

#! ruby -Ku
# mixi処理
require "rubygems"
require "mechanize"

agent=WWW::Mechanize.new
agent.max_history = 1
agent.user_agent_alias = "Windows IE 7"

agent.get("http://mixi.jp/").form_with(:name=>"login_form") do |form|
  form.set_fields(:email=>MAIL,:password=>PASS)
  form.submit
end

page = agent.get("〜〜〜〜")
agent.page.encoding = "EUC-JP-MS" # ページ取得のたびに必要

で大丈夫。でも、残念なことに、mswin32用のiconv.dllには EUC-JP-MS が無い!!

2014-02-16追記

Ruby1.9以降だと、eucJP-MSがある。また、LinuxRubyでも EUC-JP-MS じゃなくて eucJP-MS