関心空間の RSS を作成する
Weblog テクノロジを解説する上で欠かせないのが、RSS という技術です。RSS は Netscape が My Netscape Portal に採用したシンジケーション専用の XML フォーマットで、現在では Weblog にとどまらず、ニュースサイト等のサイト更新情報をシンジケートするデファクトスタンダードになりつつあります。 今回は、RSS 作成の一例として、関心空間 の HTML をパースして、RSS を生成してみます。関心空間
関心空間は、「自分が今興味を持っているモノをキーワードとして入力していくことで、その周辺情報を入手したり他の新たな関心事を発掘できるサイト」(サイトより引用) です。自分の登録したモノと、他人の登録したモノとを、「つながり」で表現することができ、サイト自体のコンセプトが Weblog や WikiWikiWeb と通じるものがあります。今回は キーワード一覧 のページを元にして、RSS を生成してみましょう(*1)。RSS の種類
ひとくちに RSS といってもいくつかのバージョンがあり、メジャーなものを簡単に分類すると以下のようになります。version | 概要 |
---|---|
0.91 | Netscape により考案され、Radio Userland により拡張されたオリジナル。Rich Site Summary の略。 |
1.0 | RDF をベースにしている。namespace によるモジュール拡張が可能。RDF Site Summary の略。 |
2.0 | RDF は複雑すぎるとして、RSS 0.91 をベースに、namespace による拡張をとりいれたもの。 Dave Winer (Radio Userland) により考案された。Really Simple Syndication の略。 |
3.0 | Aaron Swartz によるプレーンテキストなシンジケーションフォーマット。 |
サンプルコード
関心空間の キーワード一覧 の HTML を取得し、RSS を生成するスクリプトは List 1 のようになります。 このスクリプトを動作させるには、Perl 5.8.0 以上が必要です。use encoding 'euc-jp', STDOUT => 'UTF-8';Perl 5.8 より採用された encoding プラグマを使用します。今回はスクリプト自体に、サイトの HTML を解析するための正規表現(マルチバイト文字含む)を EUC-JP で埋めこんでいるため、このプラグマでその文字コードを指定します。 また、最終的に出力する RSS は UTF-8 でエンコードする必要があるため、STDOUT に UTF-8 を指定しています。これにより、ファイル内に EUC-JP で記述した文字列は、Perl 内部で Unicode 文字列として扱われ、STDOUT に print する際には自動的に UTF-8 にエンコードされることになります。
use Encode; use HTML::Entities; use LWP::Simple; use XML::RSS;動作に必要な各種モジュールを use しています。HTML エンコードのために HTML::Entities、URL から HTML の取得に LWP::Simple、また今回の主役である RSS 作成のための XML::RSS を使用します。
(my $Regex = <<'REGEX') =~ tr/\n//d; <A href="(index\.php3\?mode=keyword&id=\d+)"> <B><FONT class=css3>(.*?)</FONT></B> <IMG .*?></A> <FONT size="-1" class=css2> \d+/\d+<!-- .*? --><img .*?> \[\d+\] .*? <BR> (.*?)<BR> REGEX ;キーワード一覧 の HTML ソースを見て、アイテム情報を抜き出すための正規表現を作成します。若干複雑な HTML となっていますが、img タグのアトリビュートなど、情報として不必要なものは
.*?
で処理してしまい、余計なコードにならないように工夫しています。またこうした正規表現は通常横長になってしまいがちですが、ここでは改行を入れて見やすくした上で、
(my $Regex = <<'REGEX') =~ tr/\n//d;によって改行を除去しています(*4)。
my $rss = XML::RSS->new( version => 0.91, encode_output => 0, );XML::RSS オブジェクトを作成します。
version
は何も指定しなければデフォルトで 1.0 となりますので、ここでは明示的に 0.91 を指定します。encode_output
は、XML::RSS に渡す文字列を、出力の際に自動でXMLエンコードするかどうかのフラグで、デフォルトでは 1 となりますが、今回は HTML から拾った文字列を渡すため、二重エンコードがかかってしまう可能性があります。よって encode_output
は 0 としています。
$rss->channel( title => 'kanshin.com RSS', link => 'http://www.kanshin.com/', description => "関心空間 最新キーワードのRSS", language => 'ja', );RSS の channel 要素を設定しています。channel 要素には、RSS で配信するサイトについてのメタ情報を記述します。
my $html = decode('Shift_JIS', get($URL));最新キーワードの HTML を取得します。関心空間の HTML は Shift_JIS でエンコードされているため、Encode.pm の
decode
で、Unicode 文字列にデコードします。
while ($html =~ m/$Regex/gs) { $rss->add_item( link => encode_entities("http://www.kanshin.com/$1"), title => $2, description => normalize($3), ); }先ほど定義した正規表現
$Regex
にマッチさせます。マッチした結果は
$1 | URL (index.php 相対パス) |
$2 | アイテムのタイトル |
$3 | アイテムの見出し文 |
encode_entities
によってエンコードしています。
また description 属性には、全角スペースや改行が含まれているため、後述する normalize
関数により除去します。
if ($ENV{GATEWAY_INTERFACE}) { require CGI; print CGI::header('text/xml; charset=utf-8'); }このスクリプトは、コマンドラインで RSS を生成する目的で作成しましたが、CGI として動作させることもできるように対策してあります。CGI で動作すると、環境変数
GATEWAY_INTERFACE
に CGI/1.1
といった文字列が格納されます。ここでは、$ENV{GATEWAY_INTERFACE}
が設定されていた場合には、CGI.pm の header
メソッドにより、Content-Type: を適切に出力するようにしています。
print $rss->as_string();XML::RSS オブジェクトから
as_string
メソッドを呼び出し、STDOUT に RSS を出力します。本来であれば、print encode('UTF-8', $rss->as_string());のように、結果の文字列を UTF-8 にエンコードする必要がありますが、今回は encoding プラグマで自動的に行われるようにしてあります。
sub normalize { local $_ = shift; tr/ \r\n//d; return $_; }description 要素を正規化するサブルーチンです。
$_
に引数を格納し、tr で全角スペースと CRLF を削除しています。ここでは tr 内に全角スペースを使用していますが、先頭で encoding プラグマにより定義しているため、問題なく動作します(*6)。
実行例
コマンドラインで実行して、RSS を生成してみます(*7)。% ./kanshin_rss.pl <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE rss PUBLIC "-//Netscape Communications//DTD RSS 0.91//EN" "http://my.netscape.com/publish/formats/rss-0.91.dtd"> ... </channel> </rss>標準出力に RSS が出力されますので、リダイレクトを使用してファイルに書き出します。
% ./kanshin_rss.pl > kanshin.xml作成された
kanshin.xml
を HTTP でアクセス可能な場所に移動します。念のため、.htaccess 等で
AddType text/xml .xml AddDefaultCharset utf-8のように設定しておくとよいでしょう(*8)。 また
kanshin_rss.pl
を CGI を実行できるディレクトリ(cgi-bin など)に格納して、直接ブラウザからアクセスして RSS を出力することもできます。
まとめ
今回作成した RSS を、RSS リーダーなどのアグリゲーションツールで読み込むことにより、関心空間の新着アイテムがチェックできます。こうして既存の更新情報を RSS で取り込むと、Web ブラウザでちまちまと巡回していた手間が、RSS リーダーなどのツールに一元化され非常に便利です。Listings
List 1: kanshin_rss.pl
#!/usr/local/bin/perl -w # kanshin_rss - generate RSS for kanshin.com use strict; use encoding 'euc-jp', STDOUT => 'UTF-8'; use Encode; use HTML::Entities; use LWP::Simple; use XML::RSS; my $URL = "http://www.kanshin.com/index.php3?mode=search"; (my $Regex = <<'REGEX') =~ tr/\n//d; <A href="(index\.php3\?mode=keyword&id=\d+)"> <B><FONT class=css3>(.*?)</FONT></B> <IMG .*?></A> <FONT size="-1" class=css2> \d+/\d+<!-- .*? --><img .*?> \[\d+\] .*? <BR> (.*?)<BR> REGEX ; my $rss = XML::RSS->new( version => 0.91, encode_output => 0, ); $rss->channel( title => 'kanshin.com RSS', link => 'http://www.kanshin.com/', description => "関心空間 最新キーワードのRSS", language => 'ja', ); my $html = decode('Shift_JIS', get($URL)); while ($html =~ m/$Regex/gs) { $rss->add_item( link => encode_entities("http://www.kanshin.com/$1"), title => $2, description => normalize($3), ); } if ($ENV{GATEWAY_INTERFACE}) { require CGI; print CGI::header('text/xml; charset=utf-8'); } print $rss->as_string(); sub normalize { local $_ = shift; tr/ \r\n//d; return $_; }
*1) 執筆時点で関心空間では、RSS 等によるデータ配信は行っていません。
*2) The Atom Project
*3) 日付情報などが不完全であるため 1.0 には適さないという理由もあります。
*4) 正規表現の x オプションを使用する手もありますが、その場合、スペースを
*5) a タグの href 要素内では、 & は & にエンコードする必要がありますが、されていないサイトが多いようです。
*6) Perl 5.8.0 では encoding プラグマと tr の動作に不具合があることが報告されていますが、5.8.1 では問題ありません。
*7) 今回の URL は検索などに負荷がかかるようで、若干レスポンスに時間がかります。
*8) 一般に公開する場合は、元サイトの規約に反しないよう注意が必要です。
*2) The Atom Project
*3) 日付情報などが不完全であるため 1.0 には適さないという理由もあります。
*4) 正規表現の x オプションを使用する手もありますが、その場合、スペースを
\s
に置換する必要があります。*5) a タグの href 要素内では、 & は & にエンコードする必要がありますが、されていないサイトが多いようです。
*6) Perl 5.8.0 では encoding プラグマと tr の動作に不具合があることが報告されていますが、5.8.1 では問題ありません。
*7) 今回の URL は検索などに負荷がかかるようで、若干レスポンスに時間がかります。
*8) 一般に公開する場合は、元サイトの規約に反しないよう注意が必要です。
posted at: 20:19 by miyagawa | category: RSS | permalink
Trackback (7) | Printer Friendly | Email this blog