Phase.5 自動巡回エージェントで画像をダウンロードする(『スタパライフ』 Ver 1.10)
Chapter.21 uniBrowserを改造してHTMLを表示する
巡回エージェントで画像を収集するようになったところで、収集された画像を従来の本文と一緒に閲覧できるようにならなければ意味がありません。unibrowserでは、ログデータベースに直接格納された本文しか表示できない(*1)ので、画像をあわせて表示できるカスタムブラウザを作成します。
- unibrowserからフォームをカスタマイズする
- UIWebBrowserコンポーネントでhtml表示
- ログからhtmlページへの展開(埋め込み)
基本的な方針として、画像と本文の表示は巡回先Webサイトのイメージに近づけるため、html形式にしてブラウザで表示するようにします。
1.unibrowserからフォームをカスタマイズする
カスタムブラウザといっても、一から作り始めるのでは大変です。ここでは、これまで使ってきたairWeb標準搭載のunibrowserを元にカスタマイズします。unirowserはPlugIn\browserフォルダに置かれていますので、これを作業ディレクトリ(PlugIn\stpl)へコピーします。コピーするファイルは、unibrowser.pasと
unibrowser.dfmの2つです。
コピーすると、カスタムブラウザ用にリネーム(unibrowser
→ stplBrowser)して、Air Designerから開きます。
以下に、カスタムブラウザのデザイン構成を示します。

- unibrowserのうち、タイトルと本文の間に位置していた本文ヘッダの帯は、今回本文表示の中にレイアウトしてしまうので不要であり削除します。
- 未読数/総数の項目(Label1, Unread1)のみ上部の帯に移しています。
- HyperText1は従来本文を表示していたものですが、非表示にします。削除しないのは、スペースキーによる連続読みを行うためです。
- AWHyperTextのかわりに、新たにTUIWebBrowserを追加し、メインの表示ペインとします。
2.TUIWebBrowserコンポーネントでhtml表示
html表示をするからくりは、大雑把に言って以下のような構成になります。

- エージェント起動時に、雛型になるhtmlファイルを読み込みます。
- TitleViewはログデータベースを指定することで、ログへのアクセスが出来るようになります。
- タイトルツリーからアイテムが選ばれると、SelectItemイベントが発生します。
- エージェントのスクリプトからは、雛型になるhtml中で指定したID属性等の名前で表示されているノードにアクセスすることが出来ます。
ここで、まず雛型になるhtmlファイルを作成します。必要な情報だけに整理するなら自分の使いやすいレイアウトに構成を変えてしまうことすら可能なのですが、「スタパライフ」エージェントの場合はやはりサイトを訪れブラウザで表示したときのイメージに近い方がよいです。
そこで、大胆にも本家のページをブラウザで表示し、ソースを表示して取り込みます。(*2)
1: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
2: <html>
3: <head>
4: <meta http-equiv="Content-Type" content="text/html; charset=x-euc-jp">
5: <title>ク?トセ、キ</title>
6: </head>
7: <body bgcolor=white style="LINE-HEIGHT: 140%; MARGIN: 40px 50px">
8: <img src="stapalife.gif">
9: <p><a href="ml-5-110.html">シ。、リ</a> | <a href="ml-5-108.html">チー、リ</a> | <a href="toc5-0.html">フワシ。</a> | <a href="/">・ネ・テ・ラ・レ。シ・ク</a></p>
10:
11: <table cellpadding=0 cellspacing=0 bgcolor="#a0a0a0"><tr><td>
12: <table cellpadding=5 style="LINE-HEIGHT: 130%">
13: <tr bgcolor="azure">
14: <td nowrap width="33%">2000/11/12 02:53</td>
15: <td>ク?トセ、キ</td>
16: </tr>
17: <tr>
18: <td colspan=3 bgcolor="white" nowrap>
19: <p>
20: <BR>
21: <img src="D20001110c.gif" alt="D20001110c.gif"><BR clear=all>
22: 2000ヌッ11キ・0ニ・。、ス、ホ3<BR>
23: 。。、荀テ、ムハニ、ネ・ム・ユ・ァ、タ、ア、タ、ネ、ォ、ハ、・ュ・ト、、、ネサラ、・・・」、ハ、シ、ハ、鬢ミ。「・ム・ユ・ァ、ホ<BR>
24: エナ、オ、ヒ、隍テ、ニハニ、ホフ」、ャエーチエ、ヒシコ、・・「、ト、゙、遙「テア、ヒエナ、、ハニ、ソゥ、テ、ニ、・ネ、、、ヲセ・BR>
25: カキ、ヒ、ハ、・ォ、鬢タ。」<BR>
26: 。。、ス、ウ、ヌ、ェク?トセ、キ、ヒ・ウ・・!。。SPAM!!。。SPAM SPAM SPAM SPAM!!。。ハニ、ネ・ム・ユ・ァ、ネ<BR>
27: SPAM!!。。、ウ、・タ!!。。、ウ、・キ、ォ、ハ、、!!。。、ウ、・コョ、シ、ニソゥ、ヲ、ウ、ネ、チロチ・キ、ニ、筅ハ、ェ<BR>
28: ソゥペ、ャ、・ア、ミ。「ヒワハェ、ホテヒ!!<BR>
29: 。。、ニ、、、ヲ、ォ、ウ、ホトエサメ、ヌ、オ、鬢ヒツウ、ア、ソ、熙ケ、・ネ・ケ・ム・猗・ュ、ヒ、ハ、テ、ニ、キ、゙、、、゙、ケ、ォ<BR>
30: 。ゥ。。、「、・・ホキ、ャ、鬢サ、ヌ、ケ、ォ。ゥ<BR>
31:
32: </p>
33: </td>
34: </tr>
35: </table>
36: </td></tr>
37: </table>
38: <p><a href="ml-5-110.html">シ。、リ</a> | <a href="ml-5-108.html">チー、リ</a> | <a href="toc5-0.html">フワシ。</a> | <a href="/">・ネ・テ・ラ・レ。シ・ク</a></p>
39:
40: </body>
41: </html>
|
| http://www.alt-r.com/di/ml-5-109.html |
む! バケバケ。EUCで登録されているものをブラウザのソース表示で確認したものだから仕方がないです。「名前を付けて保存」のhtmlのみ・SJISを指定して保存する手もありますが、このくらいであれば化けていても構成はわかります。
大事そうなところを追っかけて見ましょう。
| 7行目 |
<body>タグで、背景や全体のテキストのスタイルを指定しています。そのまま残します。 |
| 8行目 |
「スタパライフ」のロゴが入っています。エージェントの外枠にでも表示するとカッコイイ感じがしますが、今回は見送り。 |
| 9行目 |
同じくナビゲーションバーがかかれています。エージェントのナビゲーションもココから行うとCoolなのですが、次のステップとしましょう。 |
| 11-13行 |
エッセイの見出し枠にあたるtableタグ、trタグがかかれています、そのまま残します。 |
| 14行目 |
日付の欄です。ココを動的に書き換えるため、tdタグの属性に、IDを追加します。 |
| 15行目 |
タイトルの欄です。同上。 |
| 18行目 |
本文の欄ですね。これも同じく。 |
| 19-32 |
グチャグチャですが、要はココに本文が入っているわけですね。この部分を挟むような形で<A
NAME="xxx"></A>というタグにしておきます。 |
| 38行目 |
9行目と同じです。今回はゴメンナサイ。 |
と、そんなわけで、サクサク削除して雛型だけにし、後から動的に内容を書き換えるところには印をつけたものにします。印は、ID属性やNAME属性でつけておくとOKです。つけても書き換えられないタグがあるかもしれませんが、そこはCut&Try。DHTMLを極めた方がそばにいらっしゃる方は、活用しましょう♪
以下のようになりました。
1:<html>
2:<body bgcolor=white style="LINE-HEIGHT: 140%; MARGIN: 40px 50px">
3:<table cellpadding=0 cellspacing=0 bgcolor="#a0a0a0"><tr><td>
4: <table cellpadding=5 style="LINE-HEIGHT: 130%">
5: <tr bgcolor="azure">
6: <td nowrap width="33%" ID="POSTDATE"></td>
7: <td ID="SUBJ"></td>
8: </tr>
9: <tr>
10: <td colspan=3 bgcolor="white" nowrap><A ID="MSGBODY">Message Body</A></td>
11: </tr>
12: <tr style="display:none">
13: <td colspan=3 bgcolor="white" nowrap ID="SENDER">あ</td>
14: </tr>
15: </table>
16: </td></tr>
17:</table>
18:</body>
19:</html>
|
| sptl.htm |
12-14行は、stplBrowserの元になった試作HTMLブラウザ「qBrowser」の名残です。エージェントから発言者を書き出していますが、「スタパライフ」には発言者の表示がありません。エージェントを直せばいいのですが、後から雛型を本家チックにした(Ver 1.22)ため非表示で残してあります。
次は、この雛型ファイルを読み込む方法です。読み込み自体は、フォームが表示されるときに実行します。全てのソースはこちらをご覧下さい。
148: // for HTML View by quwaji
149: bNavigateComplete := '';
150: bWaitNavigate := '';
151: // htmfile := DataDir + '\' + ChangeFileExt(FFileName, '.htm');
152: htmfile := ExeDir + '\' + 'Plugin\stpl\stpl.htm';
153: if FileExists(htmfile) then
154: begin
155: UIWebBrowser1.Navigate(htmfile, 0, nil, nil, nil);
156: end
157: else
158: begin
159: UIWebBrowser1.Navigate('about:blank', 0, nil, nil, nil);
160: end;
161: // end HTML View
|
| ssptlBrowser.r - TstplBrowser.FormCreate() |
FormCreate()は、フォームが生成されるときのイベントに割り当てた関数です。この中で2つのフラグをクリア(149,150行)し、TUIWebBrowserコンポーネントの
Navigateメソッドを呼び出して雛型ファイルを読み込んでいます。「スタパライフ」エージェントでは、固定ファイルを読み込んでいます。(152-156行)
Navigateメソッドによりナビゲーションが発生すると、いくつかのイベントが発生します。HTMLブラウザでは、ナビゲーションが完了した時点でもイベントを発生させます。このイベントを補足するのがNavigateComplete2メソッドです。unibrowserにはなかった処理ですので、Air
Designerでイベント処理を追加します。
434:procedure TstplBrowser.UIWebBrowser1NavigateComplete2(Sender: TObject; const pDisp: IDispatch; var URL: OleVariant);
435:var
436: Item:TTitleItem;
437:// modify-start shitara ▼▼▼初期表示メッセージのテキスト部編集・イメージ表示を追加▼▼▼
438: TmpLines: TStringList;
439: Line:String;
440: ImageFile:String;
441:// modify-end shitara ▲▲▲初期表示メッセージのテキスト部編集・イメージ表示を追加▲▲▲
442:begin
443:// modify-start shitara ▼▼▼イメージ表示を追加▼▼▼
444: ImageFile:='';
445:// modify-end shitara ▲▲▲イメージ表示を追加▲▲▲
446: bNavigateComplete := 'おっけ〜';
447: if bWaitNavigate <> '' then
448: begin
449: Item := TitleView1.Selected;
450:// modify-start shitara ▼▼▼初期表示メッセージのテキスト部編集▼▼▼
451: TmpLines:=TStringList.Create;
452: TmpLines.Text:=Item.Text;
453: // 元のヘッダを削除する(mbox & NIFTY 用)
454: while TmpLines.Count>0 do begin
455: Line := TmpLines.Strings[0];
456: if StrLIComp(Line,'X-ImageFile: ',13)=0 then begin
457: ImageFile:=Copy(Line,14,Length(Line)-12);
458: end;
459: if Length(Line)=0 then begin
460: TmpLines.Delete(0);
461: Break;
462: end;
463: TmpLines.Delete(0);
464: end;
465:// modify-end shitara ▲▲▲初期表示メッセージのテキスト部編集▲▲▲
466: if Assigned(Item) then
467: begin
468: UIWebBrowser1.document.all('SUBJ').innerHTML := Item.Subject;
469: UIWebBrowser1.document.all('POSTDATE').innerHTML := FormatDateTime('yyyy/mm/dd HH:MM', Item.Date);
470: UIWebBrowser1.document.all('SENDER').innerHTML := Item.SenderName;
471:// modify-start shitara ▼▼▼テキスト表示をプレーンテキストに変更・イメージ表示を追加▼▼▼
472:// UIWebBrowser1.document.all('MSGBODY').innerHTML := TmpLines.Text;
473: if Length(ImageFile)>0 then begin
474: UIWebBrowser1.document.all('MSGBODY').innerHTML := '<img src="' + DataDir + '\stpl\' + ImageFile + '"><br><pre>'+TmpLines.Text+'</pre>';
475: end else begin
476: UIWebBrowser1.document.all('MSGBODY').innerHTML := '<pre>'+TmpLines.Text+'</pre>';
477: end;
478:// modify-end shitara ▲▲▲テキスト表示をプレーンテキストに変更・イメージ表示を追加▲▲▲
479: end;
480: bWaitNavigate := '';
481: end;
482:
483:end;
|
| sptlBrowser.r - TstplBrowser.UIWebBrowser1NavigateComplete2() |
「スタパライフ」エージェントの場合、期待するNavigateComplete2()メソッドの呼び出しは、エージェントが起動したときの1回だけです。(*3)
| 446行目 |
ナビゲーションが完了したことを示す変数に値をセットしています。この値が設定されるまで、ブラウザにロードする雛型中のアイテムを書き換えることは出来ません。 |
| 447行目 |
もしナビゲーション完了待ちで情報を表示しなければならない状況であれば、Itemから選択されている情報を読み出し、ブラウザ上に埋め込んでいきます。埋め込みの処理自体は、次に解説するものと同じです。 |
エージェントの起動時は、実は先にAWTitleViewコンポーネントのSelectItemイベントが発生するようで、待ち合わせる必要があります。
3.ログからhtmlページへの展開(埋め込み)
Chapter (X-1).
で手を加えた巡回エージェントによって、以下のような形式のログが取得されるようになります。
0: From foo@bar Sun Nov 12 02:53:00 2000
1: From: スタパ齋藤
2: X-Number: 109
3: Date: Sun Nov 12 02:53:00 2000
4: Subject: 口直し
5: X-ImageFile: D20001110c.gif
6:
7:
8: 2000年11月10日 その3
9: やっぱ米とパフェだけだとかなりキツいと思われる。なぜならば、パフェの
10: 甘さによって米の味が完全に失われ、つまり、単に甘い米を食ってるという状
11: 況になるからだ。
12: そこでお口直しにコレ!! SPAM!! SPAM SPAM SPAM SPAM!! 米とパフェと
13: SPAM!! これだ!! これしかない!! これを混ぜて食うことを想像してもなお
14: 食欲がわけば、本物の男!!
15: ていうかこの調子でさらに続けたりするとスパム日記になってしまいますか
16: ? ある種の嫌がらせですか?
|
| stpl.mbxより抜粋 |
5行目に確認できる「X-ImageFile:
」ヘッダが今回追加されたもので、取得された画像ファイルの所在を示しています。
エージェントが本文を表示する契機は、タイトルツリー(TitleView1)でアイテムが選択されたときになります。エージェントが起動したときも同じイベントが発生します。このイベントで選択されたアイテムの情報を取得し、エージェントのUIWebBrowserコンポーネントに読み込んだsptl.htmに埋め込んでいきます。
372:procedure TstplBrowser.TitleView1SelectItem(Sender: TObject; Item: TListItem; Selected: Boolean);
373:var
374: Item:TTitleItem;
375: TmpLines: TStringList;
376: Line:string;
377:// modify-start shitara ▼▼▼イメージ表示を追加▼▼▼
378: ImageFile:String;
379:// modify-end shitara ▲▲▲イメージ表示を追加▲▲▲
380:begin
381:// modify-start shitara ▼▼▼イメージ表示を追加▼▼▼
382: ImageFile:='';
383:// modify-end shitara ▲▲▲イメージ表示を追加▲▲▲
384: Item := TitleView1.Selected;
385: if Assigned(Item) then
386: begin
387:// for HTML View by quwaji
388: if bNavigateComplete <> '' then
389: begin
390: TmpLines:=TStringList.Create;
391: TmpLines.Text:=Item.Text;
392: // 元のヘッダを削除する(mbox & NIFTY 用)
393: while TmpLines.Count>0 do begin
394: Line := TmpLines.Strings[0];
395:// modify-start shitara ▼▼▼イメージ表示を追加▼▼▼
396: if StrLIComp(Line,'X-ImageFile: ',13)=0 then begin
397: ImageFile:=Copy(Line,14,Length(Line)-12);
398: end;
399:// modify-end shitara ▲▲▲イメージ表示を追加▲▲▲
400: if Length(Line)=0 then begin
401: TmpLines.Delete(0);
402: Break;
403: end;
404: TmpLines.Delete(0);
405: end;
406: UIWebBrowser1.document.all('SUBJ').innerHTML := Item.Subject;
407: UIWebBrowser1.document.all('POSTDATE').innerHTML := FormatDateTime('yyyy/mm/dd HH:MM', Item.Date);
408: UIWebBrowser1.document.all('SENDER').innerHTML := Item.SenderName;
409:// modify-start shitara ▼▼▼テキスト表示をプレーンテキストに変更・イメージ表示を追加▼▼▼
410:// UIWebBrowser1.document.all('MSGBODY').innerHTML := TmpLines.Text;
411: if Length(ImageFile)>0 then begin
412: UIWebBrowser1.document.all('MSGBODY').innerHTML := '<img src="' + DataDir + '\stpl\' + ImageFile + '"><br><pre>'+TmpLines.Text+'</pre>';
413: end else begin
414: UIWebBrowser1.document.all('MSGBODY').innerHTML := '<pre>'+TmpLines.Text+'</pre>';
415: end;
416:// modify-end shitara ▲▲▲テキスト表示をプレーンテキストに変更・イメージ表示を追加▲▲▲
417: bWaitNavigate := '';
418: TmpLines.Free;
419: end
420: else
421: begin
422: bWaitNavigate := 'たのむよ';
423: end;
424:// end HTML View
425: end;
426: Unread1.Text := Format('%d/%d', [TitleView1.UnreadCount, TitleView1.Items.Count]);
427:
428:end;
|
| stplBrowser.pas |
ポイントを見ていきましょう。
| 378,382,395-398 |
ImageFileという変数を追加して、選択されたItemにX-ImageFileヘッダが含まれている場合、ファイル名を取得するようにしています。 |
| 388,420-423 |
bNavigateCompleteとbWaitNavigateを組み合わせて、htmlドキュメントのロード、更新が完了するのを待ち合わせています。ブラウザコンポーネントがレンダリング中にdocumentプロパティ(オブジェクト)にアクセスすると不幸が起きるためです。 |
| 406-415 |
雛型のロードが完了している/前回の表示更新が完了している確認が取れれば、ブラウザコンポーネントのdocumentプロパティから辿って、雛型で埋めた属性を頼りに情報をセットします。ImageFileにはX-ImageFileヘッダから取得したファイル名が入っていますので、その長さが0かどうかでヘッダが有ったか無かったかを判断しています。イメージファイルが有った場合には、テキスト本文を表示する前にimgタグを追加し、srcでImageFileを参照するように指定します。これで、htmlが表示されるときには画像が表示されるようになるはずです。 |
以下が最終的に巡回データを表示したカスタムブラウザエージェントです。

さて、ここでまた新たなアイデアが浮かんでいれば、嬉しい限りです。是非挑戦してみてください。
*1 AWHyperViewという、ログ本文の形式(あるいは、Content-Type:ヘッダ?)によって、ブラウザコンポーネントでhtml表示をしたりメーラ風の添付ファイルを別枠に表示するハイパーなViewerコンポーネントの提供が予定されています。
*2
サイトのソースを加工して配布するのは、本来あまりよくありません。『スパタライフ』エージェントは、サイトの許可をいただいております。また、ここで示したような加工の手順をエージェントで行うようにすれば、より汎用性が高まり、再配布の問題もなくなります。オルトアール・エッセイ汎用エージェントへと進化する際には、雛形ファイルをサイトのページから生成するようになるでしょう。あるいは、サイト運営者がエージェントや雛型ファイルを作成・提供するようになるかもしれません。AirWebの可能性の一つです。
*3 実は、v1.22では「最新の状態に更新」を実行したり、他のブラウザからリンクをドラッグアンドドロップすると発生してしまいます。特に後者は、その後TitleViewでItemを選びなおすと、ブラウザコンポーネントが保持しているhtmlデータが想定外のものになっているためエラーになってしまいます。要修正!