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 重村法克