Trackback Auto Discovery
Trackback をサポートしているBlog エントリには、"このエントリの Trackback URL" という表記がしてあります。これをコピーすれば Trackback Ping を打つ URL がわかるというわけですが、はっきりいってメンドウです。こういうものは人手でやるのではなくて、プログラムから自動でわかるようにした方が便利です。というわけで、今回は Trackback Ping URL を自動検出する方法について解説します。 ある Blog に対する Trackback Ping URL は、Trackback 仕様書 に Trackback Auto-Discovery として記述されています。 Trackback Auto-Discovery は、RDF (Resource Description Format) と呼ばれるフレームワークを利用して実装されています。RDF は、Web サイトのメタデータを Machine Readable にするという"セマンティックWeb" のベースとなる、メタデータ記述フォーマットです。 各 Blog に関連する Trackback Ping URL を、RDF データ内に記述し、 HTML 内に埋めこみます。サンプルを見た方が理解が早いでしょう。このエントリの RDF データは以下のようになります。<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/"> <rdf:Description rdf:about="http://blog.bulknews.net/cookbook/blosxom/trackback/tb_discovery.html" trackback:ping="http://blog.bulknews.net/cookbook/trackback/trackback_tb_discovery" dc:title="Trackback Auto Discovery" dc:identifier="http://blog.bulknews.net/cookbook/blosxom/trackback/tb_discovery.html" /> </rdf:RDF>このように rdf タグ内に Description という単一要素タグで、Blog エントリのメタデータについて記述しています。ここでは先頭の xmlns 宣言で、RDF 内に 3 つの名前空間を定義しています。
名前空間 | URI | 概要 |
---|---|---|
rdf | http://www.w3.org/1999/02/22-rdf-syntax-ns# | Resource Description Format |
dc | http://purl.org/dc/elements/1.1/ | Dublin Core 1.1 |
trackback | http://madskills.com/public/xml/rss/module/trackback/ | Trackback Ping |
trackback:ping="http://blog.bulknews.net/cookbook/trackback/trackback_tb_discovery"の部分で、Ping URL を指定しています。ちなみに
dc:title
にはエントリのタイトル、dc:identifier
にはエントリの Permalink を指定しています。rdf:about
については、意味的にいろいろな指定方法が考えられますが、ここでは "Trackback Ping URL についてのメタデータ" であると考え、Trackback Ping URL を指定しています。
このような RDF タグを、Blog ツールのテンプレートなどを編集して HTML 内に埋めこみます(*1)。ただ、XHTML 内にこうした RDF タグを埋めこんだ場合、User-Agent によっては表示に不具合を起こす場合があるので、この RDF を HTML エスケープするのが一般的なようです(*2)。
RSS Auto Discovery では HTML の link タグに URL を指定していたことと比べてみると面白いですね。
サンプルコード
こうして埋めこまれた Trackback Ping URL を自動的に検出するスクリプトを書くと、List 1 のようになります。このスクリプトは Trackback 仕様書 に記述されていたサンプルコードを基に、MovableType の Bookmarklet 部で使用されているものを参考にして記述しています(*3)。my $url = shift; my $find_all = shift;このスクリプトは、Blog エントリ(あるいはインデクスページ) の URL を渡すと、関連した Trackback Ping URL を表示するスクリプトになっています。引数は Blog エントリの URL と、複数の Ping URL があった場合、それをすべて表示するかどうかのフラグになります。
my @pings = discover_tb($url, $find_all); for my $ping (@pings) { print "$ping->{url}\n($ping->{title})\n"; }
discover_tb
で Ping URL を検出し、見つかれば Ping URL をタイトルとともに表示します。
sub discover_tb { my($url, $find_all) = @_; my $html = get($url) or die "Can't GET $url\n";URL を LWP::Simple を使用して HTTP GET します。取得できなければエラー終了です。
# no-anchor version for matching (my $url_no_anchor = $url) =~ s/#.*$//;
$url
のアンカー部分を削除した $url_no_anchor
を用意します。例えばエントリの URL が http://example.com/1234.html#more
等だった場合に、このアンカー部がない URL でマッチした場合にも、検出するためです。
while ($html =~ m!(<rdf:RDF.*?</rdf:RDF>)!sg) { my $rdf = $1; my $ping_url; my($permalink) = $rdf =~ m!dc:identifier="(.+?)"!;HTML から RDF 部分をマッチさせ、さらにそこから
dc:identifier
を拾って Permalink となる URL を抜き出します。
# Check if $pemalink matches to my $url next unless $find_all || $permalink eq $url || $permalink eq $url_no_anchor;Permalink が、引数となる URL と同じかどうかを比較します。これがマッチしない場合には、対象でないので次のマッチを処理します。
$find_all
(スクリプトの第2引数) が指定されているときは、Permalink にマッチするかどうかに関わらずすべての Trackback Ping URL を抜き出します。
# find Trackback URLs, fallback to rdf:about if ($rdf =~ m!trackback:ping="(.+?)"!) { $ping_url = $1; } elsif ($rdf =~ m!about="(.+?)"!) { $ping_url = $1; }
trackback:ping
属性を抜き出して Ping URL を取得します。RDF内に trackback モジュールを宣言していない場合には、rdf:about
で代用します。
my($title) = $rdf =~ m!dc:title="(.+?)"!; push @items, { title => $title, url => $ping_url }; last unless $find_all;
dc:title
でタイトル文字列を取得します。タイトルと Ping URL をハッシュリファレンスにして @items
に格納します。$find_all
がある場合は、次の RDF も処理しますが、そうでない場合は1個見つかれば OK なので、ループを抜けます。
実行例
この Blog の最初のエントリで実行してみます。% ./tb_discovery.pl http://blog.bulknews.net/cookbook/blosxom/common/readme_first.html http://blog.bulknews.net/cookbook/trackback/common_readme_first (About This Blog (Blog Developer's Cookbook))のように、エントリのタイトルと Ping URL が出力されます。インデクスページに対して実行すると、
% ./tb_discovery.pl http://blog.bulknews.net/cookbook/のように何もマッチしませんが、2番目の引数に 1 を渡せば、
% ./tb_discovery.pl http://blog.bulknews.net/cookbook/ 1 http://blog.bulknews.net/cookbook/trackback/trackback_tb_rss (Trackback Ping 一覧を RSS 出力する) http://blog.bulknews.net/cookbook/trackback/trackback_tb_receive (Trackback Ping を受信する) http://blog.bulknews.net/cookbook/trackback/trackback_tb_send (Trackback Ping を送信する) http://blog.bulknews.net/cookbook/trackback/common_wiki_created (WikiWikiWeb Created) http://blog.bulknews.net/cookbook/trackback/rss_rss2js (RSS feed を JavaScript で HTML に埋め込む) http://blog.bulknews.net/cookbook/trackback/rss_rss2htmlmail (RSS をメールで送信する (HTML メール編)) http://blog.bulknews.net/cookbook/trackback/rss_genfeed (genfeed - 汎用 RSS ジェネレータ) http://blog.bulknews.net/cookbook/trackback/opml_blogrolling2opml (BlogRolling を利用して RSS 巡回先を管理 (OPML編)) http://blog.bulknews.net/cookbook/trackback/rss_blogrolling2rss (BlogRolling を利用して RSS 巡回先を管理) http://blog.bulknews.net/cookbook/trackback/rss_rss2email (RSS をメールで送信する)のようにそのページ内に含まれるすべての Ping URL が取得できます(*4)。
Hack the Hacks
Net::TrackBack では、discover
メソッドで Trackback Ping URL の検出を行うことができます。ただし、このメソッドでは URL の抽出のみしか行うことはできず、この例のように dc:title
も一緒に取得することはできません。
Listings
List 1: tb_discovery.pl
#!/usr/local/bin/perl -w # tb_discovery - Trackback Auto Discovery use strict; use LWP::Simple; my $url = shift; my $find_all = shift; my @pings = discover_tb($url, $find_all); for my $ping (@pings) { print "$ping->{url}\n($ping->{title})\n"; } sub discover_tb { my($url, $find_all) = @_; my $html = get($url) or die "Can't GET $url\n"; # no-anchor version for matching (my $url_no_anchor = $url) =~ s/#.*$//; my @items; while ($html =~ m!(<rdf:RDF.*?</rdf:RDF>)!sg) { my $rdf = $1; my $ping_url; my($permalink) = $rdf =~ m!dc:identifier="(.+?)"!; # Check if $pemalink matches to my $url next unless $find_all || $permalink eq $url || $permalink eq $url_no_anchor; # find Trackback URLs, fallback to rdf:about if ($rdf =~ m!trackback:ping="(.+?)"!) { $ping_url = $1; } elsif ($rdf =~ m!about="(.+?)"!) { $ping_url = $1; } my($title) = $rdf =~ m!dc:title="(.+?)"!; push @items, { title => $title, url => $ping_url }; last unless $find_all; } return @items; }
*1) MovableType ではデフォルトでこの記述がテンプレートに埋めこまれます。
*2) せっかく RDF に準拠しているのに、こうやって ad-hoc に回避するのは、あまり美しくないと反発する人もいるようです。
*3) MovableType のコードは GPL 等ではないライセンスを採用しているため、そのまま引用という形にはしていません。
*4) UTF-8 で出力されるので、端末で表示できないかもしれません。その場合は
*2) せっかく RDF に準拠しているのに、こうやって ad-hoc に回避するのは、あまり美しくないと反発する人もいるようです。
*3) MovableType のコードは GPL 等ではないライセンスを採用しているため、そのまま引用という形にはしていません。
*4) UTF-8 で出力されるので、端末で表示できないかもしれません。その場合は
iconv -f utf-8 -t euc-jp
等でフィルタするとよいでしょう。posted at: 22:40 by miyagawa | category: Trackback Pings | permalink
Trackback (5) | Printer Friendly | Email this blog