2006 年 8 月 7 日 19 時 29 分

日記一覧ページを辿る


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


mixi では日記一覧ページはページャ処理されており、
30 項目ごとに分けて個別のページとなっている。

全ての日記一覧を得るためには、
全てのページを順番に取得していく必要がある。

日記一覧を管理している list_diary.pl には、
「page」というパラメータがあり、
それでページ番号を渡す仕様になっている。
先頭ページ番号は 1 となっているので、
list_diary.pl?page=1 でも最初の 30 項目が出てくる。

つまり、page パラメータに 1 から順に数値を渡せば、
日記を順番に辿ることができることになる。

では、MixiDiaryIterator を改造し、
全日記を取り出せるようにしてみよう。

========== part of MixiDiary.js ==========

// MixiDiaryIterator クラスのコンストラクタ
function MixiDiaryIterator(session) {

    // MixiSession を記憶しておく
    this._session = session;
   
    // タイトル・ID・OwnerID を取り出す正規表現
    this._rex = new RegExp(
            "<td bgcolor=#F2DDB7> ([\\w\\W]*?)</td></tr>"
            + "[\\w\\W]*?"
            + "<a href=\"view_diary.pl\\?id=(\\d+)&owner_id=(\\d+)\">続きはこちら</a>", "ig");

    // 読み込み済みのページ番号
    this._page = 0;
   
    // 見つかった日記項目(最初は空)
    this._entries = [];

}

// 次の日記項目を返す
MixiDiaryIterator.prototype.next = function () {

    // _entries が空なら次を読み込む
    if (this._entries != null && this._entries.length == 0) {
        this._getNextPage();
    }

    // _entries が null ならそれ以上ない
    if (this._entries == null) {
        throw MixiError("これ以上日記がありません");
    }

    // 日記を取り出す
    var entry = this._entries.shift();

    // 仮実装。配列を返しておく。
    return [ entry[1], entry[2], entry[3] ];
   
}

// 次の日記項目があるかどうか調べ、true/false を返す
MixiDiaryIterator.prototype.hasNext = function () {

    // _entries が空なら次を読み込む
    if (this._entries != null && this._entries.length == 0) {
        this._getNextPage();
    }

    // _entries が null ならそれ以上ない
    return this._entries != null;
}

// 次のページを読み込んで解析する
MixiDiaryIterator.prototype._getNextPage = function() {

    // ページを取得
    var html = this._session.fetch("http://mixi.jp/list_diary.pl",
            { page : this._page + 1 } );

    // 一括して日記項目を調べ、_entries に格納
    var entries = [];
    this._rex.lastIndex = 0;
    for (;;) {
        var match = this._rex.exec(html);
        if (match == null) break;
        entries.push(match);
    }

    // 見つからなかった場合は null にしておく
    if (entries.length == 0) {
        entries = null;
    }

    // OK
    this._entries = entries;
    ++this._page;
}

========== end of MixiDiary.js ==========

Iterator は、hasNext と next が順に呼ばれる事が多い。
以前の実装では、next と hasNext で、
マッチングが 2 回行われていたので少し無駄があった。

なので、今回は、ページめくりのことも考えて、
取得したページを一気に解析する方法をとることにした。

_entries 配列には、解析済みの日記一覧が含まれている。
_entries 配列が空になれば、
ページ番号を 1 増やして一覧を取得する。

ページ内に日記項目が見つからなければ、
全て取得したとみなして、_entries に null を設定する。
entries == null が、イテレータ終端の合図だ。

では、久しぶりにテストをやってみよう。

まず、MixiExporter.wsf ファイルを更新する。
ファイルを分けているので、見通しは悪くない。

========== MixiExporter.wsf ==========

<?xml version="1.0"  encoding="shift-jis" ?>
<job>
    <script language="javascript" src="./lib/XMLHttpRequest.js" />
    <script language="javascript" src="./lib/HttpUtil.js" />
    <script language="javascript" src="./lib/MixiError.js" />
    <script language="javascript" src="./lib/MixiSession.js" />
    <script language="javascript" src="./lib/MixiDiary.js" />
    <script language="javascript" src="./Main.js" />
</job>

========== end of MixiExporter.wsf ==========

そして、テストコードだ。

========== Main.js ==========

function main() {

    try {

        // ログイン(接続前に 3000 ミリ秒待機)
        var session = new MixiSession(
            "自分のメールアドレス", "パスワード", 3000);

        // 日記オブジェクトを作成
        var diary = new MixiDiary(session);

        // 日記列挙オブジェクトを作成
        var iterator = diary.iterator();

        // 項目を列挙
        while (iterator.hasNext()) {

            // 項目を取得(仮実装なので配列が返る)
            var item = iterator.next();

            WScript.Echo("================================");
            WScript.Echo("タイトル: " + item[0]);
            WScript.Echo("日記 ID: " + item[1]);
            WScript.Echo("所有者 ID: " + item[2]);
            WScript.Echo("================================");

        }


    } catch (ex) {
   
        WScript.Echo(ex.message);

    }

}

main();

========== end of Main.js ==========

main のコードは殆ど一緒だ。
変更はイテレータの中で行われているので、
呼び出し側には影響していない。

MixiSession には、サーバに接続する前に、
毎回 3 秒間 Sleep するように指定している。
まあ、3 秒もあけておけば大きな負荷にならないはずだ。

幾つかのクラスが例外を吐くようになったので、
例外処理のコードを含めている。
これで、メールアドレスやパスワードが違う場合は、
味気ないが「ログインに失敗しました。」と表示される。

さて、実行してみよう。

================================
タイトル: MixiSession の焼き直し
日記 ID: 192113767
所有者 ID: 2300658
================================
================================
タイトル: 継承の実践 #3
日記 ID: 191527366
所有者 ID: 2300658
================================

(以下省略)

全部書くとすごい量になるので省略したが、
mixi の最初の日記を含む全ての項目が列挙された。

Sleep が入っているので、実行には時間が掛かるが、
手作業よりははるかに効率が良いことに変わりはない。

次は、いよいよ本文の抜き出しだ。



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