サイトの更新情報、RSSをパースする簡易的な方法です。
このサブルーチンはWalWikiのWikiAntennaで使用しているものと、ほぼ同じものです。 エラーハンドリングもxmlnsの確認もencodingによる文字コードの判定もしていませんが、WalWikiのWikiAntennaで使用実績は積んでいて、それなりに有効であるという点は 納得してもらえるかな、と思います。 かなり簡易というかいい加減な手法なのは承知ですが、intersiteメーリングリストなどでRSSの良し悪しを論じるにも、まずはRSSを手軽に使えなきゃというのが前提だと思うので、私のできる範囲での情報公開から。
余談ですけど、私自身はニュース系サイトだけでもasahi.com、ASCII24、Biztech、CNetJapan、CNetITTrend、HotWiredNews、PCWeb、InternetWatch、Linux24、NetSecurity、NikkeiIT、NikkeiNet、ZDNet、ケータイWatchを取得(ただし多くはbulknews.net経由)していますが、特に問題は起きていません。
引数は、取得したRSSを読み込むためのファイルハンドルです。
RSSはeucコードに変換されていることを前提にしています。
sub parse_rss {
my ($rss, $num) = @_;
my @items = ();
return unless ($rss);
$num = 0 unless ($num =~ /^\d+$/);
foreach my $item ($rss =~ /<item\b.*?>.*?<\/item>/gis) {
my $parsed = {};
foreach my $tag qw(title link description dc:date) {
if ($item =~ /<$tag\b.*?>(.*?)<\/$tag>/is) {
$parsed->{$tag} = &sanitize($1);
}
}
$parsed->{'time'} = &date_to_time($parsed->{'dc:date'});
push(@items, $parsed);
last if ($num and @items >= $num);
}
return @items;
}
sub sanitize {
my $str = shift;
# remove tags and unescape
my $re_tag_ = q{[^"'<>]*(?:"[^"]*"[^"'<>]*|'[^']*'[^"'<>]*)*(?:>|(?=<)|$(?!\n))}; #'};
my $re_comment = '<!(?:--[^-]*-(?:[^-]+-)*?-(?:[^>-]*(?:-[^>-]+)*?)??)*(?:>|$(?!\n)|--.*$)';
my $re_tag = qq{$re_comment|<$re_tag_};
$str =~ s/$re_tag//g;
# resanitize
my %unescaped = ('<' => '<', '>' => '>', '"' => '"', ''' => "'", '©' => '(c)', '&' => '&');
my %escaped = ('<' => '<', '>' => '>', '"' => '"', ''' => "'", '&' => '&');
$str =~ s/&(lt|gt|quot|apos|copy|amp);/$unescaped{$1}/gio;
$str =~ s/([<>"'&])/$escaped{$1}/go;
return $str;
}
sub date_to_time {
my $date = shift;
if ($date =~ /^(\d{4})(?:-(\d{2})(?:-(\d{2})(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d))?)?(Z|([+-]\d{2}):(\d{2}))?)?)?)?$/) {
my ($year, $month, $day, $hour, $min, $sec) = ($1, ($2 ? $2 : 1), ($3 ? $3 : 1), $4, $5);
my $offset = (abs($8) * 60 + $9) * ($8 >= 0 ? 60 : -60) if ($7);
my $time = ($7) ? &Time::Local::timegm($sec, $min, $hour, $day, $month - 1, $year) - $offset
: &Time::Local::timelocal($sec, $min, $hour, $day, $month - 1, $year) - $offset;
return $time;
}
return undef;
}
返り値はハッシュリファレンスの配列で、個々のハッシュは、例えば次のような内容になっています。 項目は、元のrssの含んでいる情報によって変わりますが、通常はabout、title、linkぐらいは持っています。
{
'about' => itemの関連するURL,
'title' => itemの表題,
'link' => itemのリンクしているURL(aboutと同じケースが多い),
'description' => itemの記述(本文またはその一部であることが多い),
'dc:date' => itemの作成(とは限らない)日付([[Perlメモ/W3C形式の日時の解析]]で説明している形式)
'time' => dc:dateを1970年1月1日からの秒数に直した値(localtime等の変換にかけられます)
}
例えば上のサブルーチンを使用して、RSSからHTMLのリストに変換ということを簡単に行うことができます。 複数のRSSをパースして@itemsに保持、$item->{'dc:date'}で降順ソートして、複数のサイトの更新情報を時系列で一覧表示、といったこともできるでしょう。
my $fh;
open($fh, "http://bulknews.net/rss/rdf.cgi?NikkeiNet");
my @items = &parse_rss($fh);
close $fh;
if (@items) {
print "<ul>\n";
foreach (@items) {
print qq(\t<li>$_->{'dc:date'} <a href="$_->{'link'}">$_->{'title'}</a> - $_->{'description'}</li>\n);
}
print "</ul>\n";
}
日付はRSSから取得された際の形式そのままで出力されます。 おそらく、Perlメモ/W3C形式の日時の解析の「YukiWiki/WalWikiの日付形式に変換。」に示したスクリプトを使うなどして、書式を変換、統一した方が見やすくなるでしょう。
[[#rcomment]]