RSS Auto Discovery
あるサイトの RSS を自動で探し出すにはどのようにすればよいでしょう? "Syndicate This Site (XML)" のリンクを見て探す、でしょうか。デザインをカスタマイズしていたらどうでしょう? HTML の link タグを使用すると、RSS の URL を機械的に抽出することができます。今回はこの RSS Auto-Discovery と呼ばれる手法を実装してみます。RSS の埋め込み
「あ、このサイト便利、RSS Aggregator ソフトに追加しよう!」と思ったとき、まず必要なのは、そのサイトの RSS です。これを調べるには通常、ページ内にある [RSS] や [XML] などのアイコン、また "Syndicate This Site (XML)" などのテキストを探します。 しかしデザインをカスタマイズした Weblog だったりするとなかなか不便(*1)。数が多いときには RSS Aggregator が自動でとりこんでくれないものかな、と考えるのがエンジニア人情というものです。で、やはり考えた人がいました。- Do any RSS aggregator use the HTML LINK Element? [matt.griffith]
- RSS auto-discovery [diveintomark.org]
<link rel="alternate" type="application/rss+xml" title="RSS" href="{URL for RSS}" />のようになります(*2)。SharpReader などの RSS Aggregator, また BlogRolling や Bloglines といったメタ Blog サービス(*3)では、この機能を使って URL から自動で RSS を抜き出してくれます。よってユーザが、いちいち RSS の URL を探す手間がはぶけるというわけです。
サンプルコード
引数に URL を指定すると、その URL の中に link タグで埋めこまれた RSS の URL を調べるコマンドラインスクリプトを作成しました。コードは List 1 のようになります。# 特定のタグと属性を抽出する場合は HTML::Parser より HTML::TokeParser必要なモジュールを use します。今回は HTML のパース処理に必要な HTML::TokeParser、また URL から HTML を取得する LWP::Simple を使用します。 HTML のパースをする CPAN モジュールというと、HTML::Parser モジュールが標準的ですが、今回のように特定のタグと属性を抽出する、という目的にはあまり向きません。またこの程度の簡単な HTML 破片であれば、正規表現を使っても十分実用には耐えるとは思いますが、link タグ中の属性の順番が変わったら、など考えるといろいろやっかいですので、HTML::Parser へのトークン毎のインタフェースを提供するラッパー、 HTML::TokeParser を使用しています。
use LWP::Simple; my $url = shift; my $rss = discover_rss($url); if ($rss) { print "RSS for $url\n=> $rss\n"; } else {コマンドラインの第1引数で与えられた URL に対し、
discover_rss
を実行し、RSS の URL を取得します。取得した結果に応じて、出力結果を変えています。
} # URL から RSS を Auto-Discovery して返す # 複数の RSS Feed には未対応 sub discover_rss { my $url = shift; # HTTP GET my $html = get($url) or die "Can't get $url"; # HTML::TokeParser で link タグを取得 my $parser = HTML::TokeParser->new(\$html);
LWP::Simple::get
で 取得した HTML に対し、HTML::TokeParser オブジェクトを生成します。続いて、get_tag
メソッドで link タグを順次取得します。ここで link タグのアトリビュートは $token->[1]
にハッシュリファレンスで格納されています。ここでは、
$attr = { rel => 'alternate', type => 'application/rss+xml', href => 'http://example.com/rss.rdf', title => 'RSS', };のような値となります。よって
rel
と type
を調べ、マッチした要素の href
属性が RSS の URL となります(*4)。
実行例
コマンドラインからいくつかの URL に対して実行してみます。% ./discover_rss.pl http://blog.bulknews.net/cookbook/ RSS for http://blog.bulknews.net/cookbook/ => http://blog.bulknews.net/cookbook/blosxom/index.rss10
% ./discover_rss.pl http://www.movabletype.org/ RSS for http://www.movabletype.org/ => http://www.movabletype.org//index.xmlこのように Auto Discovery に対応した HTML を利用しているサイトでは、RSS 情報が機械的に抽出できます。 ちなみに Movable Type や blosxom では、デフォルトのテンプレートで、この RSS Auto Discovery に対応しています。その他のツールでも、自らテンプレートを編集することができれば、対応することができます。
HTML::RSSAutodiscovery
実は、このプログラムでは、車輪の再発明をしてしまいました。CPAN にある HTML::RSSAutodiscovery を使用すれば、use HTML::RSSAutodiscovery; my $url = "http://www.diveintomark.org/"; my $html = HTML::RSSAutodiscovery->new(); print $html->parse($url)->[0]->{href}, "\n";で、ほぼ同様のことが出来ます。ただ今回は、この RSS Auto Discovery の仕様を理解するために、このモジュールは使用しませんでした。
Listings
List 1: discover_rss.pl
#!/usr/local/bin/perl -w # RSS Auto-Discovery の実装 use strict; # 特定のタグと属性を抽出する場合は HTML::Parser より HTML::TokeParser use HTML::TokeParser; use LWP::Simple; my $url = shift; my $rss = discover_rss($url); if ($rss) { print "RSS for $url\n=> $rss\n"; } else { print "No RSS discovered for $url\n"; } # URL から RSS を Auto-Discovery して返す # 複数の RSS Feed には未対応 sub discover_rss { my $url = shift; # HTTP GET my $html = get($url) or die "Can't get $url"; # HTML::TokeParser で link タグを取得 my $parser = HTML::TokeParser->new(\$html); while (my $token = $parser->get_tag("link")) { my $attr = $token->[1]; # <link rel="alternate" type="application/rss+xml" href="" /> # title 要素がないサイトもいくつかあったので甘い判定 if ($attr->{rel} eq 'alternate' && $attr->{type} eq 'application/rss+xml') { return $attr->{href}; } } return; }
*1) Movable Type なら、ほとんどのユーザが Syndicate This Site (XML) を使用していますが、他のツールではそうでもありません
*2) 実際は1行で書くのが一般的です。
*3) Blog から何かの情報を抽出し、配信するサイトの総称
*4)
*2) 実際は1行で書くのが一般的です。
*3) Blog から何かの情報を抽出し、配信するサイトの総称
*4)
title
属性は使用していないサイトもあるようなのでチェックしていません。posted at: 14:45 by miyagawa | category: RSS | permalink
Trackback (4) | Printer Friendly | Email this blog