このアーカイブは同期化されません。 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 が入っているので、実行には時間が掛かるが、
手作業よりははるかに効率が良いことに変わりはない。
次は、いよいよ本文の抜き出しだ。