2006 年 8 月 8 日 18 時 51 分

日記本文の取得


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


日記は、view_diary.pl にアクセスすることで取得できる。
このページでは、完全な形で日記を見ることができる。

view_diary.pl の URL のパラメータには、
日記の ID だけでなく、著者の ID も渡す必要があるが、
これら ID は、日記一覧ページから取得できるので問題ない。

では、まずはページを取得して HTML を眺めてみよう。

    var session = new MixiSession(
            "自分のメールアドレス", "パスワード", 3000);

    var html = session.fetch("http://mixi.jp/view_diary.pl",
            { id : 192777686, owner_id : 2300658 } );

    WScript.Echo(html);

日記一覧ページからは、以下の情報を取り出す。

 ・日時
 ・本文

タイトルは、一覧ページで取得できるので、
改めてここで取得する必要はないと考える。

正規表現を考えてみよう。

日時は、2006年08月07日<BR>19:29 のようになっているので、
年・月・日・時・分 を別々に取り出して後で組み合わせる。
「(\d+)年(\d+)月(\d+)日<BR>(\d+):(\d+)」って感じだ。

本文は、「<td CLASS=h12>」で始まり、「</td>」で終わる。
「<td CLASS=h12>([\w\W]*?)</td>」って感じでいける。

では、上記のラッパ MixiDiaryEntry クラスを作り、
MixiDiary#iterator() が返すイテレータからは、
個々の日記として配列ではなく MixiDiaryEntry を返すように
設計を変更してみよう。

========== MixiDiaryEntry.js ==========

// 日記の正規表現(年・月・日・時・分・本文)
MixiDiaryEntry._REX_DIARY
        = "<td ALIGN=center ROWSPAN=2 NOWRAP WIDTH=95 bgcolor=#FFD8B0>"
        + "(\\d+)年(\\d+)月(\\d+)日<BR>(\\d+):(\\d+)</td>"
        + "[\\w\\W]*?"
        + "<td CLASS=h12>([\\w\\W]*?)</td>";

// クラスのコンストラクタ
function MixiDiaryEntry(session, id, ownerID, title) {

    // 引数評価 & 格納
    if (!(session instanceof MixiSession)) {
        throw TypeError("session 引数が不正です。");
    }
    this._session = session;
    this._id = parseInt(id, 10);
    this._ownerID = parseInt(ownerID, 10);
    this._title = String(title);

    // その他フィールド
    this._date = undefined;
    this._content = undefined;
}

// ID を返す
MixiDiaryEntry.prototype.getID = function () {
    return this._id;
}

// 日時を返す
MixiDiaryEntry.prototype.getDate = function () {
    if (this._date == undefined) {
        this._retrieve();
    }
    return this._date;
}

// タイトルを返す
MixiDiaryEntry.prototype.getTitle = function () {
    return this._title;
}

// 本文を返す
MixiDiaryEntry.prototype.getContent = function () {
    if (this._content == undefined) {
        this._retrieve();
    }
    return this._content;
}

// 日記ページを取得して解析
MixiDiaryEntry.prototype._retrieve = function () {

    // ページを読み込む
    var html = this._session.fetch("http://mixi.jp/view_diary.pl",
            { id : this._id, owner_id : this._ownerID } );

    // 解析
    var match = html.match(MixiDiaryEntry._REX_DIARY);
    if (match == null) {
        throw new MixiError("日記ページを解析できませんでした。");
    }

    // 日時は Date オブジェクトに
    this._date = new Date(match[1], match[2] - 1,
            match[3], match[4], match[5]);

    // 本文は HTML 含めてそのまま
    this._content = match[6];
}

========== end of MixiDiaryEntry.js ==========

MixiDiaryEntry は、view_diary.pl のラッパで、
MixiDiaryIterator#next() が返すオブジェクトだ。

あまり美しい設計ではないが、
Web ページへのアクセスを最小限にするために、
MixiDiaryIterator オブジェクトで取得済みである、
タイトルに関してはコンストラクタで受け取るようにした。

なので、getTitle() だけのアクセスならば、
Web ページへのアクセスは発生しない。
タイトルの一覧等を作るには便利かもしれない。

_retrieve メソッド内では、文字列である html に対して、
match メソッドを実行している。
match は、引数で与えた RegExp オブジェクトの、
exec を実行して返してくれるメソッドだが、
引数で文字列を与えた場合勝手に new RegExp してくれる。

    var rex = new RegExp(MixiDiaryEntry._REX_DIARY);
    var match = rex.exec(html);

上記と同じだが、少し手数が省けるので使ってみた。
ただ、match に文字列を渡す場合、注意点がある。

まず、RegExp のフラグが指定できないので、
ケース依存(i) の処理はできない。

そして、グローバル(g) のついた正規表現を渡した場合、
一気に文字列に対して全てマッチング(exec)を行い、
一致した部分の文字列をまとめて配列で返す。
このばあい、括弧によるキャプチャ結果は取得できない。
g の場合は exec を実行するのとは結果が異なるのだ。


さて、続いて MixiDiaryIterator#next() を書き換える。
殆ど一緒なので、一部分だけ引用する。

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

    // 日記オブジェクトを作成して返す
    return new MixiDiaryEntry(
            this._session, parseInt(entry[2], 10),
            parseInt(entry[3], 10), entry[1]);

entry にはタイトル、日記 ID、著者 ID が格納されている。
それをそのまま MixiDiaryEntry コンストラクタに渡し、
新しく作成したインスタンスを返却しているだけだ。

そして、Main.js のテストコードも一部書き換える。

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

        // 項目を取得
        var item = iterator.next();
           
        WScript.Echo("================================");
        WScript.Echo("日時: " + item.getDate().toLocaleString());
        WScript.Echo("タイトル: " + item.getTitle());
        WScript.Echo("本文: " + item.getContent());
        WScript.Echo("================================");
           
        break; // 全部やると長いのでとりあえず一個

    }

さて、実行してみよう。

================================
日時: Monday, August 07, 2006 19:29:00
タイトル: 日記一覧ページを辿る
本文: mixi では日記一覧ページはページャ処理されており、
<br>30 項目ごとに分けて個別のページとなっている。
<br>

(中略)


<br>次は、いよいよ本文の抜き出しだ。
================================

よし、問題なく実行できているようだ。
これで日記の本文が全て取得できるようになった。



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