このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。
今日は、list_diary.pl へのアクセス手段を提供する、
MixiDiary クラスを設計しよう。
MixiDiary は、各日記(項目)のコレクションの役割となる。
では、メソッドを列挙してみよう。
・コンストラクタ(session)
・iterator()
……以上。
list_diary.pl の現在の機能では、
全日記項目の数は取得できない。
そのため、getItemCount() は提供できないし、
getItem(index) のようなランダムアクセスも難しい。
となると、前から後ろに連続的にアクセスするしかないため、
その処理は列挙クラスに隠蔽することになる。
なので、メソッドは iterator() だけあれば事足りる。
getIterator() としたい所だが、Java 風にしておこう。
つまり、コレクションとしては最小限の実装となりそうだ。
Java 1.5 で言うところの Iterable インタフェース実装か。
========== MixiDiary.js ==========
// クラスのコンストラクタ
function MixiDiary(session) {
this._session = session;
}
// 日記を列挙するイテレータを作成して返す
MixiDiary.prototype.iterator = function () {
return new MixiDiaryIterator(this._session);
};
========== end of first half of MixiDiary.js ==========
ううむ。殆ど何もしてないクラスになったな。
次は列挙用のイテレータクラスだ。
実際に list_diary.pl にアクセスするのは、
このクラスの役割となりそうだ。
列挙クラスなので、メソッドは以下の通りとする。
・コンストラクタ(session)
・hasNext()
・next()
これも Java 風でいくことにしよう。
読み取り専用なので remove はないけどね。
列挙は、Java と C# で考え方が違う。
C# では、IEnumerator インタフェースで、
MoveNext() と、Current(読み取り専用プロパティ)であり、
続きがあるかどうか調査するメソッドがない。
また、Reset() という余計なメソッドがある。
逆に、Java は、現在のオブジェクトを取得するのと、
イテレータを先に進める処理が一体化してしまっている。
また、remove() という余計なメソッドがある。
個人的には、続きがあるかどうか調査、次へ進む、
現在のオブジェクトを取得の 3 セットがいいと思うのだが、
C# や Java の実装はそうはなっていない。
今回は、Java の方法を採用することにしよう。
昨日の列挙コードを埋め込んで実装すると以下のようになる。
MixiDiaryIterator は MixiDiary に密接に関係するので、
同じファイルの中に続けて入れることにする。
========== latter half of MixiDiary.js ==========
// MixiDiaryIterator クラスのコンストラクタ
function MixiDiaryIterator(session) {
// MixiSession を記憶しておく
this._session = session;
// list_diary.pl を取得
this._html = session.fetch("http://mixi.jp/list_diary.pl");
// タイトル・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");
}
// 次の日記項目を返す
MixiDiaryIterator.prototype.next = function () {
if (this._html == null) {
// 本来は NoSuchElementException って感じ
return null;
}
var match = this._rex.exec(this._html);
if (match == null) {
this._html = null;
// 本来は NoSuchElementException って感じ
return null;
}
// 仮実装。配列を返しておく。
return [ match[1], match[2], match[3] ];
}
// 次の日記項目があるかどうか調べ、true/false を返す
MixiDiaryIterator.prototype.hasNext = function () {
if (this._html == null) {
return false;
}
var lastIndex = this._rex.lastIndex;
if (!this._rex.test(this._html)) {
this._html = null;
return false;
}
this._rex.lastIndex = lastIndex;
return true;
}
========== end of MixiDiary.js ==========
_html フィールドには HTML を入れておき、
g フラグを指定した正規表現で列挙する。
g(global) フラグを指定した場合、
exec メソッドで検査が成功すると、
正規表現の lastIndex プロパティが更新される。
そのため、昨日のような繰り返しの場合は、
意識せずに exec() が null を返すまで呼び出せば良い。
イテレータとして実装する場合、
チェックのための、hasNext メソッドが必要だ。
このメソッドは調査だけなので、状態を変えてはいけない。
正規表現の検査は、exec() の他に、test() がある。
test() は、正規表現に一致した場合に true を返すのだが、
単純に exec() を呼んで、戻り値が null かどうか
調べてるだけなので、lastIndex が更新されてしまう。
そのため、lastIndex プロパティの値を記憶しておき、
test 後に、元に戻す作業が必要となる。
また、exec() を呼び出して列挙が終了した際は、
無駄なマッチングを繰り返さないように、
_html フィールドをクリアしておき、
これが null であれば、false を返すようにしておこう。
ではテストコードを書く。
========== Main.js ==========
function main() {
// ログイン
var session = new MixiSession(
"自分のメールアドレス", "パスワード");
// 日記オブジェクトを作成
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("================================");
}
}
main();
========== end of Main.js ==========
うむ。シンプルになった。
では、実行してみよう。
================================
タイトル: 一覧表示と詳細表示
日記 ID: 187248888
所有者 ID: 2300658
================================
================================
タイトル: 正規表現
日記 ID: 186493095
所有者 ID: 2300658
================================
(以下省略)
よし、まずは問題なさそうだ。
list_diary.pl の 1 つのページから得られる情報は少ない。
明日以降は、何度かページにアクセスして、
日記を取得していく必要がある。
自動化プログラムとしての MixiSession の制約、
そしてエラー処理、ページャ処理など、
考えることは山ほどある。
少しずつ潰していくか。