mecanize.rb で mixi diary を自動取得する
Posted by yoosee on Web at 2005-09-02 23:42 JST1 WWW::Mechanize alike in ruby
WWW::Mechanize は Webページに対して、ブラウザからのアクセス的な処理を自動化して行える ruby library。例えばと言ったアクセスの動作を記述できる Ruby library 。元は Perl に同名の WWW::Mechanize があり、それの ruby 版と言った立ち位置のようだ。Web Test フレームワークで同様の機能を持つものは結構あるが、それをプログラム内から使えるようにライブラリ化したものと考えればいいかも。http://example.com にアクセス → 1番目の form の各フィールドに foo, bar, baz を入力して submit → ページ内の "foo" を含む link を列挙してクリック → ページを取得
2 WWW::Mechanize alike in ruby を使って mixi にアクセス
mixi へ login した後に新着日記の URL 一覧をとるには、以下のようにすればいい。require 'mechanize' username = 'foobar@example.com' password = 'foobarbaz' agent = WWW::Mechanize.new {|a| a.log = Logger.new('access.log') } page = agent.get('http://mixi.jp/') form = page.forms[0] form.fields.find {|f| f.name == 'email'}.value = username form.fields.find {|f| f.name == 'password'}.value = password form.fields.find {|f| f.name == 'next_url'}.value = '/home.pl' page = agent.submit(form, form.buttons.first) if /url=([^"])"/ =~ page.body link = 'http://mixi.jp' + $1.to_s agent.get(link) end diarylist = agent.get('http://mixi.jp/new_friend_diary.pl') diarylist.links.each do |link| diaryurl = link.href # => 'view_diary.pl?id=12345&owner_id=2345' endといった感じ。page.body が html text を持つので文字列操作などをしたいときはこれを使うと良い。
3 mechanize.rb と net/smtp を同時に require → トラブル
しかし便利なだけかと思ったら微妙な落とし穴と言うか不具合が。mechanize を使いつつ net/smtp を使うと、Net::SMTP.start(host) ... が/usr/lib/ruby/1.8/net/smtp.rb:393:in `do_start': private method `open' called for Net::InternetMessageIO:Class (NoMethodError)などというエラーを吐くんである。net/smtp のみを使った場合はなんの問題もなく、mechanize を require すると発生する問題のようだ (さらっと書いているが突き止めるのに数時間かかった…)。
調べてみると、mechanize には mechanize/net-overrides/protocols.rb と言うファイルがあり、mechanize.rb の先頭で load-path に上記を追加している。これにより純正の net/protocols.rb が上書きされており、Net::InternetMessageIO を全く完全に再実装しているのがトラブルの元らしい。どうやら経過措置のつもりのようだが、少なくとも ruby1.8.2 の標準 net/protocols との組合わせでは mechanize は動作しない。結局 system から mailx を呼ぶという逃げを打ってしまったが、こんなのどう解決するのが正しいんだろう? 恐らくこのままだと net/smtp だけでなく、net/protocols を使う他の net/* ライブラリも影響を受けるだろうし、load-path をいじられてると分離も難しそう。mechanize 内部の require や Net::* を色々いじれば逃げられるだろうか。
mechanizeさわってみましたが、難しいですね。