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 が無い!!