mechanizeを使ってreadonlyのフィールドに無理やり値をセットするサンプル

久しぶりに MEET THE WORLD BEAT に行かなかったbonlifeです。ライヴジャンキー失格です。MINAMI WHEEL 2007には行きます。
さてさて、会社のログインをmechanizeで自動化して、あれこれしようと思っていたところ、hidden になってるフィールドへの値の設定で躓いてしまったので、その時のメモです。
まず、以下のようにしてログインを試みました。

In [1]: import mechanize

In [2]: br = mechanize.Browser()

In [3]: br.set_proxies({})

In [4]: br.addheaders = [('User-agent', 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)')]

In [5]: br.open('https://www.example.com/login')
Out[5]: <response_seek_wrapper at 0x12b79b8 (0) whose wrapped object = <closeable_response at 0x12b7800 whose fp = <socket._fileobject object at 0x0122DF10>>>

In [6]: br.select_form(nr=0)

In [7]: br['username'] = 'foo'
---------------------------------------------------------------------------
<type 'exceptions.ValueError'>            Traceback (most recent call last)

C:\...\<ipython console> in <module>()

C:\...\build\bdist.win32\egg\ClientForm.py in __setitem__(self, name, value)

<type 'exceptions.ValueError'>: control 'username' is readonly

proxy設定がらみの対処法については「sh1.2 pyblosxom : mechanizeを使ってみる」を参考にしました。JavaScriptを使ってクライアントサイドでUser-agentをチェックしている画面なので、addheaders でIE6の気分になりまして。で、select_formして、usernameをセット…と思ったら、ナニコレ、readonlyって!
formは実際のところどうなってんのよ、と思ってチェック。

In [8]: print br.form
<POST https://www.example.com/login application/x-www-form-urlencoded
  <HiddenControl(username=) (readonly)>
  <HiddenControl(password=) (readonly)>>

readonly…。HTML上で <input type="hidden"> になってるフィールドはreadonlyとして扱われる様子。ググってみても、Perlの場合のやり方しか見つからず。(ググり方が悪いのかもしれません。)

    {
        local $^W = 0;
        $agent->field( name => $value );
    }

一度諦めて、翌日。そういや、mechanize って ClientForm を使ってたよな、と思って調べ直す。どうやら control には readonly って属性があって、そこは変更可能っぽい。タブキーをアチコチで連打して探っている内にたどりついたのが以下の方法!

In [9]: for i in br.form.controls:
  ....:     i.readonly = False
  ....:     

In [10]: print br.form
<POST https://www.example.com/login application/x-www-form-urlencoded
  <HiddenControl(username=)>
  <HiddenControl(password=)>>

キタコレ!readonlyじゃなくなってるよ、ママン!ということで、早速、ログイン再チャレンジ。

In [11]: br['username'] = 'foo'

In [12]: br['password'] = 'bar'

In [13]: br.submit()
Out[13]: <response_seek_wrapper at 0x10734e0 (0) whose wrapped object = <closeable_response at 0x123f490 whose fp = <socket._fileobject object at 0x01325D18>>>

おぉおぉ。無事にログイン出来ましたよ。このあたりをもうちょっとキレイにまとめて、社内でこっそり広めよう、そうしよう。mechanizeに乾杯!