にたまごほうれん草アーカイブ

はてなダイアリーで書いてた「にたまごほうれん草」という日記のアーカイブです。現在は「にたまごほうれん草ブログ」を運営中です。

WWW::MechanizeでPOSTするデータを直接書くための野良拡張

先日のニコニコ動画のコメント取得スクリプトでは、WWW::Mechanizeを使用していましたが、最後にコメントを取得する部分だけはNet::HTTPクラスを直接使用していました。

comments = Net::HTTP.start(comment_host, 80) {|http|
  response = http.post(path, body)
  response.body
}

というのは、WWW::MechanizeではPOSTリクエストはクエリをハッシュか配列でしか渡せないため、コメント取得のためのリクエスト文字列をボディに直接書くことができないためです。

body = %!<thread res_from="-500" version="20061206" thread="#{thread_id}" />! # こいつ

しかし、せっかくWWW::Mechanizeを使っているので最後までこのままやりたいところです。
というわけで、勝手に直接データを書けるように拡張しました。

# file: mechanize-ext.rb
require 'mechanize'

module WWW
  class Mechanize
    self.class_eval {
      def post_data(url, data='', enctype=nil)
        cur_page = current_page || Page.new( nil, {'content-type'=>'text/html'})
        request_data = data
        abs_url = to_absolute_uri(url, cur_page)
        request = fetch_request(abs_url, :post)
        request.add_field('Content-Length', request_data.size.to_s)

        page = fetch_page(abs_url, request, cur_page, [request_data])
        add_to_history(page)
        page
      end
    }
  end
end

これで、

require 'mechanize-ext'
agent = WWW::Mechanize.new
body = '<hoge foo="bar"/>'
res = agent.post_data('http://example.com/api', body)

こんなリクエストも送ることができます。(たぶん、ニコニコでしか試してないけど…)
ちなみに、レスポンスは、WWW::Mechanize::Fileのオブジェクトなので、文字列を取り出すときはres.bodyで。
リクエストフィールドをもっと加えたい場合は、request.add_fieldすればいいと思います。

追記

post_dataの引数enctypeを使っていないことに気づいた。(でも修正はしていない)