2006 年 8 月 6 日 21 時 28 分

MixiSession の焼き直し


このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。


例外クラスの設計が終わったところで、
MixiSession クラスを自動化プログラム用に改変しよう。

今回盛り込むのは以下の要素だ。

・通信失敗、ログイン失敗などの例外処理
・連続リクエストを防ぐための待ち処理

まず、コンストラクタだ。
待ち時間はコンストラクタで外部から受け取る事にしよう。
引数に wait を追加して、ミリ秒単位の数値を受け取る。

そして、コンストラクタで行っているログインは、
失敗したときに例外を投げるようにする。

mixi の login.pl は、ログインの成否に関わらず、
HTTP ステータスは、「200 OK」として HTML を返却するが、
ログインに失敗した場合は Cookie を返却しない。
それを使ってログイン失敗を判定することにしよう。

では、コンストラクタを作ってみる。

    // クラスのコンストラクタ
    function MixiSession(mailAddress, password, wait) {

        // リクエストを発行する間隔
        if (wait == undefined) wait = 0;
        this._wait = parseInt(wait, 10);
        if (this._wait < 0) this._wait = 0;

        // 内部で利用する XMLHttpRequest を作成
        this._client = new XMLHttpRequest();

        // ログイン用のパラメータを用意し、
        var form = {
            next_url: "/home.pl",
            email: String(mailAddress),
            password: String(password)
        };

        // ログイン処理にアクセスして Cookie を得る
        this.post("https://mixi.jp/login.pl", form);

        // ログインに成功すれば Cookie が返るはず
        var cookie = this._client.getResponseHeader("Set-Cookie");

        // Cookie が空ならログイン失敗
        if (cookie.length == 0) {
            throw new MixiError("ログインに失敗しました。");
        }
    }

wait パラメータは数値として _wait プロパティに入れる。
また、post メソッドを呼び出した後、
サーバが返却した Cookie を調べる。

Cookie は、Set-Cookie というレスポンスヘッダとして、
サーバから返却されるので、その中身を取得し、
中身がなければ失敗として例外を送出する。

fetch や post メソッドでは、HTTP による接続をする前に
単純に wait ミリ秒待機することにしよう。
本当は、同一サーバに対するリクエストの場合だけでいい。

そして、XMLHttpRequest#send で送信した後は、
サーバが返した HTTP ステータスコードを確認し、
200 系(=成功)以外の場合はエラーとする。
100 や 300 はとりあえず考えないことにしよう。

このあたりは手抜き実装ということで、まあいいでしょ。

    // GET を発行して結果の HTML を返す
    MixiSession.prototype.fetch = function (url, query) {

        // リクエスト前に指定ミリ秒待機する
        WScript.Sleep(this._wait);

        // クエリパラメータを用意
        var content = HttpUtil.encodeForm(query);
        if (content.length > 0) {
            content = "?" + content;
        }

        // GET で送信
        this._client.open("GET", url + content, false);
        this._client.send("");

        // HTTP ステータスを検証
        this._validateStatus();

        // テキストを返却
        return this._client.responseText;
    };

    // POST を発行して結果の HTML を返す
    MixiSession.prototype.post = function (url, form) {

        // リクエスト前に指定ミリ秒待機する
        WScript.Sleep(this._wait);

        // リクエストボディを用意
        var content = HttpUtil.encodeForm(form);

        // POST で送信
        this._client.open("POST", url, false);
        this._client.setRequestHeader("Content-Type",
            "application/x-www-form-urlencoded");
        this._client.send(content);

        // HTTP ステータスを検証
        this._validateStatus();

        // テキストを返却
        return this._client.responseText;
    };

    // ステータスコードが 200 系でなければ例外
    MixiSession.prototype._validateStatus = function () {
        if (Math.floor(this._client.status / 100) != 2) {
            throw new MixiError(
                    "サーバが想定外の応答を返しました。"
                    + "[HTTP " + this._client.status.toString()
                    + " " + this._client.statusText + "]");
        }   
    };

ざっくりとこんな感じだ。

WScript には、Sleep メソッドがあるので、
これを使えば指定して時間だけ待機することができる。
Sleep ばスレッド自身を完全に止めてしまうが、
自動化プログラムでは特に問題とならないはずだ。

そして、エラーのチェックをしているのが、
_validateStatus メソッドだ。

サーバの返す HTTP ステータスコードは、
status プロパティに数値として格納されているので、
100 で割って切り捨てた値が 2 かどうかで判断する。
JavaScript の / は、小数演算なので、
Math.floor を使って切り捨てを行う必要がある。

因みに、statusText には、
サーバが返す HTTP ステータスの文字列が入っている。
例えば、200 OK なら、「OK」が statusText だ。
エラーの場合、理由が分かるようにメッセージに含めよう。

明日は、これを使って、mixi から、
ページの連続取得を試してみよう。

このスクリプトでは、サーバのページを取得する際に、
外部の CSS や JavaScript、画像などを取得しないので、
適切な待機時間をおいてアクセスするのならば、
ブラウザで mixi にアクセスするよりも、
サーバに発行するリクエストは少ないはずだ。



Copyright (c) 1994-2007 Project Loafer. All rights reserved.