それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

Mechanizeでパースに失敗するHTMLを何とかする。

HTMLの閉じタグが抜けていたり、HTMLの構造がおかしいサイトがある。これをMechanizeすると困ることになる。MechanizeがParseできなくてこまるんです。つまりNokogiriがエラーになったり、Nodeがウマく取れなくてFormのInputが無かったり、有るべきリンクがなかったりする。それは困る。

だけど、HTMLのミスは結構どうしようもない。

HTMLのミスに対応しようと思うと面倒。HTMLの受け渡しは、Mechanizeの本体で行なわれてるので、本気で対応しようするとと結構面倒な事になる。間違ってMyMechanizeクラス作りかけてた。あらためてMechanizeのヘルプを読み直すと、パーサーだけを入れ替える例を見つけたのでコレでErrorがあるHTMLに対応してみた。

プラグインでHTMLエラーに対応する。

MyPageクラス
class MyParser < Mechanize::Page
  def initialize(uri=nil, response=nil, body=nil, code=nil, mech=nil)
    super(uri, response, body, code, mech)
    #その他HTMLソースを弄くるのはココに書く
    # たとえば多すぎるDiv閉じを減らす
    body.sub! %'</div></div></div></div>' , '</div></div>' 
  # たとえば formの飛び先をHTMLソースレベルで変える
    body.sub! %'<form action=hoge.php>' , '<form action=hoge2.php>'
  end
end
agent.pluggable_parser.html = MyParser #parserを差し替える

使い方

require 'kconv'
require 'mechanize'
require 'MyPage.rb'
agent = Mechanize.new
agent.pluggable_parser.html = MyParser #parserを差し替える
agent.get(url) #HTMLにミスがあるページURL
puts agent.page.body.to_html.toutf8 #=> 置換済みのHTMLが出てくる

ruby はオープンClassなので、Mechanize::Page#initializeを再定義しても良いんだけど、ソース読んで分かりにくくなるのでやめた。

動きの解説

プラグイン機構でPageを自分のMyPageに差し替える。Formクラスは、init on Readですね。

なので、page.formsが呼び出されて初めてHTMLソースコードを解析し、Newする。なのでFORMSが作られる前のタイミング(この場合はinitialize)でソースコードをかきかえておくのでした。

閉じ忘れ以外の対応

HTMLのタグ閉じ忘れや、ネストがおかしい場合は、コレで対応できる。