2006.01.02 RSS Feedネタ nobutaka さん指令により, RSS Feed ネタに挑戦してみる。前々か ら XML::RSS については名前だけは知っていたので, 使い方を勉強 してみる。 SEE ALSO: http://perldoc.jp/docs/modules/XML-RSS-1.02/lib/RSS.pod 色々ごにょごにょ試行錯誤を繰り返し, 使い方に慣れた結果, channel と add_item と save だけ知ってればいいや。ということ が分かった。更に parsefile 使えばファイル置いておいて後から ごにょごにょという技が使えることがわかり, どうせ channel な んて変わらんし, 一々 Jcode.pm で文字変換かますのも面倒なので テンプレートとして置いておくことにしました。 その際, セキュ リティメモの RSS を参考に調整を行ってみました:-)。 SEE ALSO: http://www.st.ryukoku.ac.jp/~kjm/security/memo/memo.rdf ※現時点(2006/01/02)での topic.rdf のテンプレート(もちろん UTF-8 で保存) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Nork's daily how to http://blog.ninth-nine.com/ Nork's daily how to 略して「のうはう」 ja Copyright 2006, Norikatsu Shigemura 2000-08-23T07:00+00:00 nork@ninth-nine.com nork@ninth-nine.com 日記 1 hourly 2006-01-01T00:00+09:00 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - このテンプレートから, item を加えたファイルを生成するわけで すが, opendir-readdir-closedir して…な perl script をでっち あげるだけなのでプログラムのほとんどは調整に尽きるという…。 ついでに自動的に index.html を生成するようにした。 ※現時点(2006/01/02)での ndex.html, topic.rdf 生成スクリプト - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #!/usr/bin/perl use strict; use Jcode; use XML::RSS; use HTML::Template; use POSIX "strftime"; sub min($$) { return $_[0] > $_[1] ? $_[1] : $_[0]; } sub getdir() { return sprintf("%s/htdocs/diary", $ENV{PREFIX} ); } sub getpath($) { return sprintf("%s/%s", getdir(), $_[0]); } sub geturl($) { return sprintf("http://blog.ninth-nine.com/diary/%s", $_[0]); } sub main { my $rss = new XML::RSS(version => "1.0"); $rss->parsefile(sprintf "%s/html/topic.rdf", $ENV{PREFIX}); my $template = new HTML::Template( filename => sprintf("%s/html/index.html", $ENV{PREFIX}), die_on_bad_params => 0, case_sensitive => 1, loop_context_vars => 1, global_vars => 1, ); opendir(DIR, getdir()); my @item; while( defined(my $file = readdir(DIR)) ) { if( $file =~ /^\w/ and -f getpath($file) ) { my @s = stat(_); printf "%s %s\n", strftime("%Y/%m/%d %H:%M:%S", localtime($s[9])), $file; open(FILE, getpath($file)); $_ = scalar ; chomp; my(undef, $title) = split /\t/, $_, 2; push @item, { file => $file, path => getpath($file), url => geturl($file), title => jcode($title, "euc")->utf8, time => $s[9], timestamp => strftime("%Y/%m/%d %H:%M:%S", localtime($s[9])), URL => sprintf("/diary/%s", $file), TITLE => $title, }; } } @item = sort {$b->{time} <=> $a->{time} } @item; if( $item[0]->{time} > time - 1800 ) { foreach my $item ( @item[ 0 .. min($#item, 14) ] ) { $rss->add_item( title => $item->{title}, link => $item->{url}, # description => "", ); } $rss->save(sprintf "%s/htdocs/topic.rdf", $ENV{PREFIX}); $template->param("ITEMs" => \@item); open(OUTPUT, sprintf(">%s/htdocs/index.html", $ENV{PREFIX})); print OUTPUT $template->output(); close(OUTPUT); } } # main &main; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - まぁ解説するだけのことはないだろうけど, 『一言言いたい』類の ことは多々あるので, コメントしておく。 1. strftime ラヴ 世の中の perl script には localtime を配列得たものを @time = localtime; $sec = "0" . $time[0] if( $time[0] < 10 ); などとする, 見るもおぞましいコードが氾濫している。 最低でも $sec = sprintf("%02d", $time[0]); するべきだし, そもそも strftime 使えば, 望みのフォー マットで時間は得られる。というわけで私は strftime が 推奨する。世の中 Perl Module 使えないよ。とおっしゃる 方がいるが, 住んでる世界の公理系が違うので接触するこ ともできないだろう(POSIX モジュールは Perl の標準モジ ュールです)。最低でも下記のイディオムを控えておくべき です。 use POSIX "strftime"; # この当たりは qw(stftime) でもいいが.. $datetime = strftime("%Y/%m/%d %H:%M:%S", localtime); 2. sprintf ラヴ 世の中 sprintf で清書すればいいものを . で連結すると いうおぞましいコードが氾濫している。 $path = $dir . "/" . $file; せめて $path = "${dir}/${file}"; あたり希望したいところだ(コードが小さすぎて気持悪さ 加減が変わらないのが残念だが)。 $path = sprintf("%s/%s", $dir, $file); もっともポータビリティを考えると File::Spec モジュー ル使って use File::Spec::Functions; $path = catfile($dir, $file); という話になりますが, このあたりの匙加減は微妙なとこ ろ。目的に応じて使い分けるのがいいかもしれない(そも そも $dir の中身がポータビリティあるのかという問題が)。 3. main 関数は男のロマン(爆)。 main の無いプログラミングはできませんとかなんとか。 冗談はともかく, main 関数抜けるとき my で宣言した変数 が綺麗に消えてくれるので重宝してます。main 関数抜けて 残ってる変数があれば, そりゃリークでしょう。ってこと で(実際はリークしてなくても)。use strict; と合わせて 無意識に暗黙のダメダメコードを排除するするためのプロ グラミング作法とういうことで。。。 4. インデントは 4 文字 趣味の問題なので敢えて簡潔に。タブは 8 文字。というこ とで:-)。ていうか, インデントの無いソースは(ry。そう いう意味では Python はよくわかってらっしゃる:-)。 5. デバッグコードはインデント無視 というか, すぐ消せるように行頭に持って来てる。まぁ趣 味主張の類。後で消そうね:-)。 6. 使ってないコードが沢山 将来を見据えて用意してあります(爆)。ということにして おいてください。どうも RSS に記事の最終更新日時とい う情報は要らんようで, わざわざ準備した私が以下略。 7. 色々テストしてたら bsddiary で top になってました。 それもコンテンツ更新無しで(爆) ということで, 30 分以内に更新されたファイルが無い場 合, index.html も topic.rdf も更新しないようにしま した。bsddiary は 5, 35 分の 2 回アクセスがあるので 3, 33 分の 2 回チェック・更新を行うようにします。 8. ディレクトリ構成 Apache 設定のこだわり。を話すときりがないので, とり あえず簡単に紹介しておきます。1 サーバーで 1 サイト しか運用しない場合でも, 敢えて下記のように構成して います。ついでに PREFIX 環境変数にサイトのトップデ ィレクトリを設定しています。 SetEnv PREFIX /www/blog.ninth-nine.com え゛? make_index.pl は cron から呼び出されるんじゃな いかって? そりゃ env PREFIX=/www/blog.ninth-nine.com make_index.pl してるに決まってるぢゃん(爆)。 /www/ + blog.ninth-nine.com/ + htdocs/ + index.html + topic.rdf + diary/ + .....txt + .....txt + libexec/ + make_index.pl + html/ + index.html (テンプレート) + topic.rdf (テンプレート) ======================================================= 検証の話。 nobutaka さんにお願いして topic.rdf のチェックを行って いただきました。とりあえず問題無いらしい。その後, sarumaru さんから http://feedvalidator.org/ を教えて いただきました。サーバーからの応答で US-ASCII で答えて いるよ。ということがわかり, 急遽 AddCharset UTF-8 .rdf で逃げました(Apache 設定ファイルへの追加)。 SEE ALSO: http://feedvalidator.org/check.cgi?url=http%3A%2F%2Fblog.ninth-nine.com%2Ftopic.rdf このあたり, RFC3023 によれば MIME 型は application/rdf+xml であるのが妥当なようで, 拡張子も .rdf であるべきのよ うです。幸い Apache の mime.type ファイルにはこの設定が 載ってるので, charset さえ設定できれば完璧と。。 SEE ALSO: http://www.ietf.org/rfc/rfc3023.txt また w3m によりヘッダ情報を目視で確認できます。 $ w3m -dump_head http://blog.ninth-nine.com/topic.rdf ======================================================= 結論。 というわけで RSS Feed 使ってみてください。 SEE ALSO: http://blog.ninth-nine.com/topic.rdf Written by 重村法克