SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

PerlでデスクトップCGI

デスクトップCGIでWebとデスクトップを融合する 第3回

独自の日記記述言語からHTML形式のWeb日記を出力・配信する


知識循環型データベースと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イメージ例を示します。

筆者 Web 日記の「Small World|スモールワールド」検索出力例
筆者 Web 日記の「Small World|スモールワールド」検索出力例

 検索結果のHTML出力については、次の「renewal_r2html.pl」の最後の部分を参照してください。

「renewal_r2html.pl」 Web日記の検索結果からHTMLを出力するPerlスクリプト
#!/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サイトに配信するスクリプトです。これをツールとしてエディタに設定しておくと便利です。

「ftp2site.pl」 エディタ上からWeb日記のHTMLをWebサイトに配信するPerlスクリプト
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]を選択し、クリックしてください。

TeraPadのツール設定 - ftp2site.pl
TeraPadのツール設定 - ftp2site.pl

まとめ

 本連載の第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の再利用については別の機会に取り上げたいと考えています。

次の記事

参考資料

  1. デスクトップCGIでWebとデスクトップを融合する 第1回 Firefox/Apache用AtomリーダーCGIの作成
  2. デスクトップCGIでWebとデスクトップを融合する 第2回 Web日記のAtom配信
  3. Jperl
  4. Index of /ActivePerl (APi522e.exe)
  5. jperlユーザーのために
  6. TeraPad: ToClip for Windows
  7. DANA - The Advanced Text Processor for Windows
  8. TeraPad ツールについて
  9. Microformats
  10. Google マップ ヘルプ
  11. Firefox - Web の再発見
  12. Mozillaでルビ表示3 - 徒書
  13. 筆者サイト「日曜プログラマのひとりごと」

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
PerlでデスクトップCGI連載記事一覧

もっと読む

この記事の著者

jscripter(ジェイスクリプター)

NIFTY SERVEのFGALEL(エントリーラボ)、FGALTS(テキスト&スクリプト)に参画し、FGALTSでシスオペを務める。現在、TSNETメンバー。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/570 2007/02/22 12:31

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー