古いMechanizeスクリプトを少しモダンにする

まずは定義

古い
Ruby 1.8.5 + Mechanize 0.8 + Hpricot 前提のもの
モダン
Ruby 1.9 + Mechanize 1.0 + Nokogiri 前提のもの

Mechanize 2.0 は新しすぎるので手を出しません。

WWW::Mechanize -> Mechanize 変更

Mechanize は 1.0 で WWW ネームスペースがなくなりました。これは機械的に置き換えます。個人的には常に以下のような subclass を用意していたので、この作業はそれほど大変ではありませんでした。

# -*- coding: utf-8 -*-
=begin
agent = EzDebug_Mechanize.new( :debug => true,
:page_dir => /path/to/dir ) { |a|
a.log = Logger( '/path/to/logfile' )
a.log.level = Logger::DEBUG
}
LICENCE : two-clause BSD
=end
require 'www/mechanize'
class EzDebug_Mechanize < WWW::Mechanize
def initialize( params = {} )
super()
@debug = nil
@page_dir = nil
opt = {
:debug => false,
:page_dir => nil
}.merge( params )
if ( opt[:debug] )
@debug = true
@page_dir = opt[:page_dir] if ( opt[:page_dir] )
end
end
def fetch_page( params )
page = super
if ( @debug and page.is_a?( WWW::Mechanize::File ) and @page_dir )
pagepath = Object::File.join( @page_dir,
sprintf( "%03i_%s",
@history.size, page.filename ) )
page.save_as( pagepath )
Object::File.open( pagepath + '.mech', 'wb' ) { |f|
f.write( page.inspect )
}
end
return page
end
end

Bundler でまとめて gem のバージョンを管理

gem のバージョンを gem メソッドで指定できることを知って以降、とにかくあの面倒くさい時期の Mechanize に触れないように

gem 'mechanize', '< 0.9'
require 'mechanize'
require 'hpricot'

とかやっていたのですが、個々のファイルでこのように書いていると書き直しや gem のインストールが面倒なので Bundler でまとめてしまいます。具体的には

Gemfile

を用意して、

gem 'mechanize', '< 2'

などとします。で、

bundle install

します。

system の gem を汚さない方法

もし system wide な gem に変更を加えるのが難しい場合1

bundle install --path vendor

のようにして、変更を加えるコードの場所に gem をインストールします。これ以降の作業は全部

bundle exec ./script

みたいな形で行います。この場合は利用する gem は基本的に全部 Gemfile に書いておく必要があるので、

gem 'rake'

などを書き足す必要があるかもしれません。

最初のネック - Hpricot と Nokogiri の encoding の扱いの違い

  1. Hpricot ベースの Mechanize はパース時にもページの encoding はそのままスルー
    • form や link の検索時には "ラベル".tosjis などのように変換が必要
  2. Nokogiri ベースの Mechanize はパース時の encoding はすべて UTF-8 に統一
    • UTF-8 でコードを書いている場合は form や link の検索時には何も考える必要なし ex) link_with( "ラベル" ).click
    • ただしパース前の Mechanize#page は元の encoding のまま保存されている。

到達したページから何らかの情報を取得したい場合は一度手で UTF-8 に変換しないとややこしいです。特に Nokogiri に渡し直す場合は UTF-8 への変換が必須になります。

Hpricot から Nokogiri に切り替える

  • 何はともあれ全部 UTF-8 化する
# -*- coding: utf-8 -*-
$KCODE='u' unless defined? ::Encoding

を付けて回りましょう。もちろん関連ファイルは全部 UTF-8 に統一します。もうさすがに ja_JP.eucJP な環境はない…ですよね?

  • 文字コード変換をどんどん削除
link_with( :text => 'ラベル'.tosjis ).click
-> link_with( :text => 'ラベル' ).click

これで内部で UTF-8 のパース済みデータを持っている Mechanize と比較的仲良くできます。

恐らく、当初思っていたよりは面倒は起きずになんとかなると思います。

パースに失敗する場合

もっと詳しいところがあるのでどうぞ!

Ruby 1.9 対応

上の作業だけで Mechanize 1.0 + Nokogiri 1.5 に対応できていたら、そのまま Ruby 1.9 に対応できる可能性は比較的高いです。ぜひ rvm などから ruby のバージョンを切り替えてテストしてみることをお勧めします。

ただし、1.9 ではどうしてもうまく動かない場合もあります。(いま実際にそれでハマっているコードも手元にあります。これをどうするかは今後考えます。)

  1. そんなことあるのかな? 

More