知識循環型データベースとWeb日記検索結果のHTML出力 - renewal_r2html.pl
「知識循環型データベース」とは作成者と利用者が一致しているようなデータベースを意味します。日記も時系列的な思索記録(thinking log)と考えれば、一種の知識循環型テキストデータベースと考えることができます。ここでは、過去の日記記事を新しい記事にリンクする場合に、過去の日記を検索してHTMLで出力するスクリプトを考えてみましょう。
「renewal_r2html.pl」は、「ddl2html.pl」で出力するWeb日記をインターネットから取得して検索します。筆者のサイトの日記を読むように設定してあるので、TeraPadの編集画面などに出力して試すことができます。このスクリプトも「ddl2html.pl」と同様の方法で、TeraPadのツールとして設定できます。
ツールの実行方法は簡単です。TeraPadの編集画面で、行頭に、
[k: 検索キーワード正規表現パターン;s: 検索開始年;e: 検索終了年;o: (0|1)]
を記述して、記入行を範囲選択して、ツールとして実行します。例えば、
[k: Small World|スモールワールド;s: 2004;e: 2006;o: 0]
とした場合は、正規表現パターン「Small World|スモールワールド」にマッチする記事を2004年から2006年の範囲の日記記事から検索して、HTMLのリスト形式で表示します。検索する日記ファイル名は、「日記名_西暦4桁_月2桁.html」の形式を前提としています。例えば、筆者のサイトの日記は、「renewal_2006_08.html」のようなファイル名で月単位で管理されています。「o: 0」とした場合は、パターンにマッチした行を出力しません。「o: 1」とすると、パターンマッチした行にマッチした順に番号を付けてパラグラフとして出力します。出力HTMLイメージ例を示します。
検索結果のHTML出力については、次の「renewal_r2html.pl」の最後の部分を参照してください。
#!/Perl/bin/MSWin32-x86-object/jperl.exe use strict; use LWP::Simple; # 日記システム関連の設定 my $diary_name = "更新日記";# 日記名 my $diary_filename = "renewal";# 日記ファイルベース名 my $diary_file = "${diary_filename}.html";# 日記ファイル名 # 日記ファイルを置くサイト URL my $siteurl = "http://homepage1.nifty.com/kazuf/"; my $fyear = 2001; # 日記を付け始めた年 my $fmon = 3; # 日記を付け始めた月 # メモ格納ディレクトリのあるディレクトリのフルパス my $docroot = $ENV{'DOCROOT'}; my $memodir = "memo";# メモ格納ディレクトリ名 my $catstr;# カテゴリーキー文字列 my $category;# カテゴリー名 my %categories;# カテゴリー連想配列 if(open(IN, "<$docroot/$memodir/category.txt")){ while(<IN>){ chomp; ($catstr, $category) = split(/\t/); $categories{$catstr} = $category; } close(IN); }else{ # category.txt が無ければ、次の連想配列のデータを使う %categories = ( "rn" => "更新履歴", ); } my $kw;# キーワード正規表現パターン my $byear;# 検索開始年(西暦4桁) my $eyear;# 検索終了年(西暦4桁) my $outsw;# 検索マッチ行出力の有無(0: 無、1: 有) while(<>){ chomp; if(/^\[k: (.+?);s: (.+?);e: (.+?);o: (\d)]$/){ $kw = $1; $byear = $2; $eyear = $3; $outsw = $4; }else{ print "Error: ",$_, "→ 行頭に、[k: パターン;s: 開始年;e: 終了年;o: 0]\n"; exit; } } # 現在の日付時刻の取得 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)
= localtime(time); if($byear > $fyear && $byear - 1900 <= $year){ $fyear = $byear; $fmon = 1; } $eyear = $eyear - 1900; if($eyear < $year && $eyear >= $fyear){ $year = $eyear; $mon = 12; } my $iy;# 年度のカウンタ my $im;# 月のカウンタ my $immin;# 開始月 my $immax;# 終了月 my @dfiles;# 日記ファイル名配列 for($iy=$fyear;$iy<=$year+1900;$iy++){ # 年が日記を付け始めた年ならば、 if($iy == $fyear){ $immin = $fmon;# 日記を付け始めた月から $immax = 12; # 12月まで # 年が今年の }elsif($iy == $year + 1900){ $immin = 1; # 1月から $immax = $mon;# 今月まで # 間の年ならば、 }else{ $immin = 1; # 1月から $immax = 12;# 12月まで } for($im=$immin;$im<=$immax;$im++){ $im =~ s/^(\d)$/0$1/;# 1桁月の場合は10の位に0を付加する # 日記ファイル名を配列に格納していく push(@dfiles, "${diary_filename}_${iy}_${im}.html"); } } push(@dfiles, "${diary_filename}.html");# 今月のファイル名 # 日記ファイル名とサイト名から、URL を生成し、 # LWP::Simple のget関数で内容を取得する。 my $dfile;# 日記ファイル名 my $htmlurl;# 日記ファイルURL my $html;# 取得HTML my @content;# HTML行配列 my $insw;# 日記部分を示すフラグ my $line;# 日記行 my $date;# 日記日付整形 my $subject;# 日記表題 my $count;# 同一日付記事カウンタ my %anchor;# 検索パターンマッチ行取得用連想配列 foreach $dfile (sort @dfiles){ $htmlurl = $siteurl . $dfile; if($html = get $htmlurl){ # 改行で分割して配列に格納 @content = split(/\n+/, $html); }else{ next; } # 行単位で解析 $insw = 0; foreach $line (@content){ # 日記の日付を取得 if($line =~ /^<DT>(\d+)\/(\d+)\/(\d+)[^\d]*<DD>(.*)$/i){ $date = sprintf("%4d/%1.2d/%1.2d",$3,$1,$2); if($4 ne ""){ $category = ""; $subject = $date; } $insw = 1;# 日記部分を示すフラグを立てる $count = 0;# マッチ回数の初期化 # 日記のカテゴリと主題を取得 }elsif($line =~ /^<div class=.+<A NAME="([^"]+)">
([^<]+)<\/A><\/div> /i){ $category = "#".$1;$subject = $2; # 主題にリンクがある場合のカテゴリと主題の取得 }elsif($line =~ /^<div class=.+<A NAME="([^"]+)">([^<]*)(?:
<A HREF="[^"]+">)*([^<]+)(?:<\/A>)*([^<]*)<\/A><\/div>/i){ $category = "#".$1;$subject = $2.$3.$4; # </
DL>タグで日記を抜ける }elsif($line =~ /^<\/DL>$/i){ $insw = 0;# フラグを降ろす last;# foreach ループを抜ける } $line =~ s/<.+?>//g;#HTMLタグを削除 # 日記部分であり、かつ検索パターンにマッチすると # マッチした部分を赤いフォントに設定して、 # 日付、ファイル名、フラグメント、主題をキーに格納する if($insw == 1 && $line =~ /$kw/io){ while($line =~ s/(>[^><]*)($kw)(?!<\/font>)/$1<font color="red">$2<\/font>/i){ ; } if($line !~ /[<>]/){ $line =~ s/($kw)/<font color="red">$1<\/font>/ig; } $count++; $anchor{"$date\t$dfile\t$category\t$subject"}
.= "<p>($count) $line</p>\n"; } } } # マッチした記事部分を整理して、HTML 出力 my $key;# マッチ行のキー(タブ区切りの日付、ファイル名、カテゴリー、主題) print "<ul>\n<li>${diary_name}記事の「$kw」検索結果\n\t<ul>\n"; foreach $key (sort keys(%anchor)){ ($date,$dfile,$category,$subject) = split(/\t/,$key); ($catstr = $category) =~ s/^#([^_]+)_\d+$/$1/; print "\t<li><a href=\"${dfile}$category\">
[$categories{$catstr}]$subject</a> ($date)\n"; if($outsw == 1){ print $anchor{$key}; } } print "\t</ul>\n</ul>\n";
生成したHTMLファイルのFTP配信 - ftp2site.pl
「ftp2site.pl」は、エディタ上から、編集したWeb日記のHTMLファイルをWebサイトに配信するスクリプトです。これをツールとしてエディタに設定しておくと便利です。
use strict; use Net::FTP; # your.ftp.siteにftpサイト名を記載してください。 my $ftpsite = 'your.ftp.site'; my $home = '/your_directory';# ディレクトリを指定してください。 my $ftp = Net::FTP->new($ftpsite); print "${ftpsite}に接続しました。\n"; # ftpのアカウントとパスワードを記載してください。 $ftp->login('ftp_account','ftp_password'); print "loginしました。\n"; $ftp->cwd($home); print "${home}ディレクトリに移動しました。\n"; $ftp->ascii(); print "asciiモードにします。\n"; $ftp->put($ARGV[0]); print "$ARGV[0]を転送しました。\n"; $ftp->quit(); print "FTPを終了しました。\n"; sleep(1);
ツール設定は次の設定画面を参考にしてください。作業フォルダには、編集しているHTMLファイルがあるフォルダを指定します。スクリプトも一緒に置くか、スクリプトにパスを通しておけば、-Sスイッチでスクリプトが検索されるため、他のフォルダに置くこともできます。このスクリプトはjperlで動作させます。コマンドラインパラメータの%nはファイル名(ロングネーム)を意味します。実行はHTML編集画面で、右クリックして、[ツール]-[ftp2site]を選択し、クリックしてください。
まとめ
本連載の第1回では、Firefoxのブックマークに登録したAtomフィードのリストから内容を読み取るAtomリーダーをCGIで作成しました。第2回は筆者のWeb日記を題材にして、Atomフィードを配信するデスクトップCGIを作成しました。本稿では、さらにさかのぼって、日記記述言語DDLを利用して筆者のWeb日記のHTMLを出力する方法を示しました。いずれのスクリプトも基本的には、定型的な書式を持つテキストからパターンマッチで必要な部分を取り出し、新しい構造を持つテキストを生成するだけです。今回はエディタ上でPerlのようなスクリプト言語を利用すると、インテリジェントで極めて複雑な編集を行える可能性があることを感じていただけたと思います。
このようにHTMLの編集と配信、HTMLからXML(Atom)への変換と配信、Webを流通する定式化されたXML(Atom)の取得とHTML変換表示などにPerlが自在に利用できます。このようなスクリプトを組み合わせてシステム化すると、CMS(Contents Management System)を作ることが可能になり、デスクトップやインターネットにある情報をもとに、Webコンテンツを生成して配信する環境を構築することができます。
今後の展開
連載第4回となる次回は、リレーショナルデータベースにAtomフィードを蓄積して、検索表示やカテゴリー別配信等、再利用する方法を考える予定です。次回で、デスクトップCGIでWebとデスクトップを融合する基盤となる技術はほぼすべて出そろいます。連載第5回はAtomリーダーにRSSリーダーを加え、RSS/Atomを統合したリーダーを作ります。Firefoxのブックマークとインターネットエクスプローラのお気に入りを統合して取り扱うことも計画しています。ここまで到達すれば、高度で実用的なアプリケーションをデスクトップCGIで作れることが証明できるでしょう。また、microformatsの再利用については別の機会に取り上げたいと考えています。
次の記事
参考資料
- デスクトップCGIでWebとデスクトップを融合する 第1回 Firefox/Apache用AtomリーダーCGIの作成
- デスクトップCGIでWebとデスクトップを融合する 第2回 Web日記のAtom配信
- Jperl
- Index of /ActivePerl (APi522e.exe)
- jperlユーザーのために
- TeraPad: ToClip for Windows
- DANA - The Advanced Text Processor for Windows
- TeraPad ツールについて
- Microformats
- Google マップ ヘルプ
- Firefox - Web の再発見
- Mozillaでルビ表示3 - 徒書
- 筆者サイト「日曜プログラマのひとりごと」


e: 終了年;o: 0]\n";
