Published on Blog Developer's Cookbook.
Printer friendly version of http://blog.bulknews.net/cookbook/blosxom/trackback/tb_send.html.


Trackback Ping を送信する

by miyagawa at Fri, 05 Dec 2003 03:54

なにかのイベントや、事件などについての Blog 記事を書くとき、同じ内容について書かれている Blog エントリ同士がリンクでつながっていると、ブラウズやディスカッションの際に非常に便利です。Trackback は、こうしたエントリ間のつながりを、簡単な URL 呼び出しで実現するための Blog API です。今回は、この Trackback Ping の API について簡単に解説し、Trackback Ping を実際に送信するコードを記述してみます。


TrackbackMovableType の Ben & Mena 夫妻によって考案され(*1)、Movable Type ではじめて実装された、Blog 間の言及通知のフレームワークです。

Trackback は、送信側が受信側に対して、HTTP によるメッセージを送信することによって行われます。

まず送信側は、通知したい Blog エントリやカテゴリ毎に固有の Trackback URL に対して、HTTP POST で
パラメータ意味
urlURL
blog_nameBlog 名
titleタイトル
excerpt概要
の情報を送信します。受信側は、これらのパラメータをローカルのDB などに保存し、レスポンスを以下のような XML で返します。

Content-Type: text/xml

<?xml encoding="utf-8"?>
<response>
  <error>0</error>
  <message>Ping saved successfully.</message>
</response>

もしエラーが起こった場合には、

Content-Type: text/xml

<?xml encoding="utf-8"?>
<response>
  <error>1</error>
  <message>Something Bad happened.</message>
</response>

のように error に 1 をセットします。

Trackback の API はたったこれだけです。MovableType では、各記事やカテゴリごとに Trackback の固有ID が用意されており、http://example.com/mt/mt-tb.cgi/1234 のような URL が Trackback URL (送信先)となります(*2)。その記事やカテゴリにリンク、言及した Blog がこの Trackback URL に対し、上述のような HTTP POST リクエストを送信します。

ここで紹介したように、Trackback の主な利用目的は、「リンクしたよ」とか、「同じイベントにいった感想を書いたよ」という通知といえますが、他にも

  1. 複数の Blog サイトの特定カテゴリのエントリを集約する
  2. PhotoFridayFridayFive のような、アンケートなどの結果集積所
といったような利用方法が考えられます。実際、Movable Type では、「特定のカテゴリにエントリを記述した際に、関連づいた Trackback URL に Ping を送信する」という設定を行うことができるようになっています。

サンプルコード

Trackback を送信するための簡易フォームつき CGI スクリプトを記述すると、List 1 のようになります。

use CGI;
use HTTP::Request::Common;
use LWP::UserAgent;
使用するモジュールを use します。CGI スクリプトとして起動するため、CGI.pm を使用しています。

my $query = CGI->new();
   $query->charset('utf-8');
CGI オブジェクトを new し、charset メソッドで文字コードを UTF-8 としています。Trackback の文字コードについては、実際の Trackback規格仕様書では対応方法が記述されておらず、日本語のように複数の文字エンコーディングを持つ言語での対応が若干混乱しています。執筆時点では、 というのが国内の各種 Blog ツールのラフコンセンサスとなっていますので、ここでは UTF-8 で送信することにします。TypePad のようなホスティングサービスでも、UTF-8 で Ping を送れば文字化けすることはないようです。

my $mode  = $query->param('mode');
if ($mode && $mode eq 'form') {
    show_ping_form($query);
CGI の mode パラメータを受けとり、form であれば、送信するためのフォームを表示する show_ping_form を呼び出します。

} else {
    my $v = $query->Vars();
    if ($v->{ping_url} && $v->{title} && $v->{url}) {
        my $message = send_ping($v);
        show_ping_form($query, $message);
    } else {
        show_ping_form($query, "invalid parameters");
    }
}
mode がない場合には、Ping 送信モードになります。パラメータ ping_url, title, url が指定されているかチェックし、問題なければ send_ping により Trackback Ping を送信します。送信した結果は $message に格納されるので、フォームと同時に画面に出力します。

パラメータに不備がある場合も、その旨メッセージがフォームとともに表示されます。

sub show_ping_form {
    my($query, $message) = @_;
    $message ||= '';
    print $query->header, <<HTML;
<html>
<body>
$message
<form>
Ping URL: <input type="text" name="ping_url" size="30" /><br />
Title:    <input type="text" name="title" size="30" /><br />
Blogname: <input type="text" name="blog_name" size="30" /><br />
Blog URL: <input type="text" name="url" size="30" /><br />
Excerpt:  <input type="text" name="excerpt" size="50" /><br />
<input type="submit" value="Send" />
</form>
</body></html>
HTML
    ;
}
Trackback を送信するための簡易フォームを表示します。

sub send_ping {
    my $v = shift;
    my $ua = LWP::UserAgent->new();
       $ua->agent("tb_send/1.0");
    my $req = POST $v->{ping_url}, [
        title     => $v->{title},
        url       => $v->{url},
        blog_name => $v->{blog_name},
        excerpt   => $v->{excerpt},
        charset   => 'utf-8',
    ];
    my $response = $ua->request($req);
    unless ($response->is_success) {
        return "HTTP error: " . $response->status_line;
    }

    $response->content =~ m!<error>(\d+).*<message>(.+?)</message>!s;
    return $1 ? "Error: $2" : "Ping to $v->{ping_url} successful";
}
send_ping では、LWP::UserAgent オブジェクトを生成、CGI パラメータから POST リクエストを作成して、request メソッドによりリクエストを送信します。先述したように、charset パラメータに utf-8 をセットしていることに注意してください。

HTTP レスポンスがエラーであった場合には、ステータス行とともに return します。HTTP レスポンス自体が成功した場合は、返り値の XML 内の error 属性の値を調べ、1 であった場合にはエラー、0 であった場合には Ping 成功として表示します(*3)

実行例

このスクリプトを、CGI 実行可能なパスに設置し、ブラウザから .../tb_send.cgi?mode=form として呼び出してみましょう。 Trackback Ping フォーム このようなフォームが表示されます。一番上の Ping URL に、Trackback Ping 先の URL (mt-tb.cgi/1234 など)、以下の4つには、Ping 元となるエントリ(通常は自分の Blog エントリでの記述内容) を入力し、Send のボタンを押します。

Ping が成功すれば、

Ping to http://example.com/tb.cgi successful.

失敗した場合には、

HTTP error: 500 Internal Server Error.

といったメッセージが表示されます。

Hack the Hacks

Trackback のリファレンス実装である tb-standalone が MovableType サイト内でオープンソースで公開されています。このツールの中にも、Trackback を送信するモードが用意されていますそのままでは日本語に対応していません。筆者の Blog で日本語対応版を公開しています。

CPAN モジュール Net::TrackBack は、リファレンス実装をモジュール化して使いやすくしたものです。

No Host Trackback のように、Trackback に対応していない Blog ツール向けのホスティングサービスも用意されています。

今回用意したスクリプトを、誰でもアクセスできる場所に置くと、Trackback SPAM の踏み台にされる可能性もありますので、注意しましょう。

Listings

List 1: tb_send.pl
#!/usr/local/bin/perl -w
# tb_send - Send Trackback Ping

use strict;
use CGI;
use HTTP::Request::Common;
use LWP::UserAgent;

my $query = CGI->new();
   $query->charset('utf-8');

my $mode  = $query->param('mode');
if ($mode && $mode eq 'form') {
    show_ping_form($query);
} else {
    my $v = $query->Vars();
    if ($v->{ping_url} && $v->{title} && $v->{url}) {
        my $message = send_ping($v);
        show_ping_form($query, $message);
    } else {
        show_ping_form($query, "invalid parameters");
    }
}

sub show_ping_form {
    my($query, $message) = @_;
    $message ||= '';
    print $query->header, <<HTML;
<html>
<body>
$message
<form>
Ping URL: <input type="text" name="ping_url" size="30" /><br />
Title:    <input type="text" name="title" size="30" /><br />
Blogname: <input type="text" name="blog_name" size="30" /><br />
Blog URL: <input type="text" name="url" size="30" /><br />
Excerpt:  <input type="text" name="excerpt" size="50" /><br />
<input type="submit" value="Send" />
</form>
</body></html>
HTML
    ;
}

sub send_ping {
    my $v = shift;
    my $ua = LWP::UserAgent->new();
       $ua->agent("tb_send/1.0");
    my $req = POST $v->{ping_url}, [
        title     => $v->{title},
        url       => $v->{url},
        blog_name => $v->{blog_name},
        excerpt   => $v->{excerpt},
        charset   => 'utf-8',
    ];
    my $response = $ua->request($req);
    unless ($response->is_success) {
        return "HTTP error: " . $response->status_line;
    }

    $response->content =~ m!<error>(\d+).*<message>(.+?)</message>!s;
    return $1 ? "Error: $2" : "Ping to $v->{ping_url} successful";
}
*1) Feature: Trackback が、Trackback をはじめて紹介しているエントリと思われます。
*2) もちろん他の Blog ツールでも同様に記事ごとに固有の Trackback URL が用意されていることが多いようです。
*3) 簡易のため正規表現で拾っています。XML::Simple などを使用してもいいですが、これだけシンプルな XML にわざわざパーサをつかうのは、鉛筆をノコギリで削るようなもので、ちょっとオーバースペックな気もします。

Copyright©2002-2003 Tatsuhiko Miyagawa