RSSを配布せよ!

Pingを送信する!

PingサーバとXML-RPC

さて、次はブログで一般的になった「Ping送信」だ。これは

  1. 「コンテンツに更新があったかどうか」を一括で管理するサーバに、「Ping」と呼ばれるリクエストを送る。
  2. そうすると「Pingサーバ」がそれを管理していて、「いつ更新したか」を教えてくれる

というものである。個別に RSS を取得しなくても、一つのサーバにアクセスするだけで「いつ更新されたか?」を知ることができるわけだ。

そういう「Pingサーバ」はネット上にいくつもある。でこいつらのAPIは大体共通したものがあり、そうそう特殊なことはない。しかし、そういうサーバの案内ページを見ても、「MovableTypeの場合は...」とかいう説明ばっかりで、「手動でPingを送る!」なんていうニーズはあまり満たしてくれない。なのでちょいとここで書くことにしよう。

実際にはこの「Pingサービス」自体は、XML-RPC で実装される(のが普通)。まあ、どんなやり方でやろうとも、似たようなサービスの実現は可能なんだが、イマドキのWebアプリ話で XML-RPC が使われている...ということになっている。

まあ、それでも「XML-RPC って何だ??」という向きもあるだろうから、まずそっちから説明すると、こいつは原始的な SOAP みたいなものだ。SOAP に関しては別なページでいろいろ説明しているから、細かいことはそっちを見て欲しいが、要するに「HTTP を使って XML を送って、RPC(リモート・プロシージャ・コール)を実現する」というものだ。そこらへん SOAP と違いはないんだが、「けど SOAP は仕組みが大げさ過ぎるぞ、もっとシンプルで充分やん!」という向きがあるわけで、意外に「逆進化」してしてしまい、「Web API」というと難しいSOAPじゃなくて、結構この単純な XML-RPC が使われているケースも多いわけである。

まあ、実際 SOAP だと、

POST /soap/servlet/rpcrouter HTTP/1.0
Host: localhost:80
Content-Type: text/xml; charset=utf-8
Content-Length: 433
SOAPAction: ""

<?xml version='1.0' encoding='UTF-8'?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SOAP-ENV:Body>
<ns1:ping xmlns:ns1="urn:weblogUpdates"
   SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<message xsi:type="xsd:string">ホームページの概要</message>
<url xsi:type="xsd:string">http://www.nurs.or.jp/~sug/homep/</url>
</ns1:ping>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

というような名前空間のかなりややこしいリクエストを発行するのだが、XML-RPC の場合はもっと単純で、

POST /rpc HTTP/1.0
Host: localhost:80
Content-Type: text/xml; charset=utf-8
Content-Length: 210

<?xml version="1.0"?>
<methodCall>
  <methodName>weblogUpdates.ping</methodName>
  <params>
    <param><value>ホームページの概要</value></param>
    <param><value>http://www.nurs.or.jp/~sug/homep/</value></param>
  </params>
</methodCall>

のようにシンプルなもので済むわけだ。

ここで重要なのは「weblogUpdates.ping」である。これが「外部に対して公開されるメソッド」という格好になっており、ネット越しに「weblogUpdates.ping() というメソッドを実行する」(ような)感じなのである。で、このメソッドの引数が2つ(両方とも文字列)あって、第1引数が「ホームページの概要(大概 message というような名前)」、第2引数が「そのURL(大概 url というような名前)」ということになる。これで「どのページが更新された!」という情報をサーバに登録し、サーバがその「登録時間」を内部で属性として与えて保存し、それをまた後で外部からチェックできる、という寸法だ。

lwp-request

まあ、このページでは「どうやって Ping サーバを作るのか?」という話題は扱わず、「既存の Ping サーバに自動的に送信する」という方だけを扱おう。で、具体的にどうやるか?という話だが、なにせ相手さんのあることである。「いきなりコーディング!」というわけにはいかない。とりあえずプログラムを書く前に、「ターゲットとするPingサーバが、どういうやり取りをするか?」というのを確認する必要がある。

そのために便利なツールが lwp-request である。

いろいろと Perl で仕事しちゃってるだろうから、多分 LWP パッケージは入れているものと思う。そうすると、これをインストールしたんなら、 lwp-request という「コマンド」も一緒にインストールされるはずだ。ちょいと皆さんのパスで探して見ればいいが、/usr/local/bin とか /usr/bin とかにあるんじゃないかと思う。これは LWP パッケージのクライアントに相当するスクリプトなので、スタンドアロンで使える。こんな感じで起動する。そうすると標準出力に結果を吐く。当然これもXMLでのコンテンツである。一応見やすさのために整形するが、実際にはまったく改行なしで返る(EOF改行もなし...)。

$ lwp-request -m POST -c text/xml http://ping-server.jp/rpc < temp.xml
<?xml version="1.0"?>
<methodResponse>
  <params><param>
    <value><array>
      <data>
         <value>
            <boolean>0</boolean>
         </value>
         <value>Thanks&#32;for&#32;the&#32;ping.</value>
       </data>
     </array></value>
  </param></params>
</methodResponse>

この lwp-request のオプションは見て大体見当が付くだろう。要するに

-m オプション
で「POST/GET/HEAD」などのメソッドを指定し、
-c オプション
で Content-type: ヘッダを指定する。

でこのスクリプトは標準入力から POST メソッドの「Content」に相当する内容(XML-RPC に食わせる XML)を食わせてやればいいわけだ。

まあ、レスポンスは見れば見当が付くだろうけど、これがエラーした場合(たとえば例外を投げるとか..)には、次のようになる。

$ lwp-request -m POST -c text/xml http://ping-server.jp < temp.xml
<?xml version="1.0"?>
<methodResponse>
  <fault>
    <value><struct>
      <member>
        <name>faultString</name>
        <value>java.lang.Exception:&#32;java.lang.RuntimeException:&#32;
          Not&#32;ERROR,&#32;but&#32;test</value>
      </member>
      <member>
        <name>faultCode</name>
        <value><int>0</int></value>
      </member>
   </struct></value></fault>
</methodResponse>

これで実際の Ping サーバの挙動を確認して、このやり取りを直接 LWP で模倣してやればいいわけである。

Ping送信CGI

じゃあ、実際 RSS を更新するCGIで、今 lwp-request コマンドでやったことを模倣してやる、という方向で実装しよう。こんな感じか。まあ、今回はレスポンス結果は「成功か失敗か?」だけで、内容がそう「重要!」ってほどでもない。真面目にやるとレスポンスの判定は面倒なので XML ベースじゃなくて、タダの正規表現一致で手を抜いている。

use Unicode::Japanese;
use LWP::UserAgent;
use HTTP::Request;
use HTTP::Response;

# message に入れる内容
$BLOGNAM{'exp'} = "三木淑生と実験映像";
$BLOGNAM{'takara'} = "杉浦と宝塚歌劇";
$BLOGNAM{'soft'} = "杉浦とソフトウェア開発";
$BLOGNAM{'homep'} = "杉浦とホームページ製作";

# url に入れる内容
$URL{'exp'} = "http://www.nurs.or.jp/~sug/exp/";
$URL{'takara'} = "http://www.nurs.or.jp/~sug/takara/";
$URL{'soft'} = "http://www.nurs.or.jp/~sug/soft/";
$URL{'homep'} = "http://www.nurs.or.jp/~sug/homep/";

# 送信されるべき Ping サーバー
# リストなのでテキトーにコンマ区切りで複数指定可
@PingServers = ( "http://ping.bloggers.jp/rpc/" );

$ping_res = &ping_servers( $category );
$Output =~ s/#PING_RESULT#/$ping_res/g;

###################################################
sub ping_servers {
    my($category) = @_;
    local($server, $ret);
    foreach (@PingServers) {
	$server = $_;
	$ret .= ping( $category, $server );
    }
    return $ret;
}

sub ping {
    my( $category, $server ) = @_;
    local($xml,$desc,$url,$ret,$info,$text);
    $desc = $BLOGNAM{$category};
    $url = $URL{$category};

    # XML の構築
    $xml = << "EOM";
<?xml version="1.0"?>
<methodCall>
  <methodName>weblogUpdates.ping</methodName>
  <params>
    <param><value>#MESSAGE#</value></param>
    <param><value>#URL#</value></param>
  </params>
</methodCall>
EOM
    $xml =~ s/#MESSAGE#/$desc/;
    $xml =~ s/#URL#/$url/;
    $xml = Unicode::Japanese->new($xml,'euc')->get;
    
    local( $req, $ua, $resp, $ret );
    # LWP オブジェクトの生成
    $ua = new LWP::UserAgent;
    $req = new HTTP::Request 'POST', $server;

    ヘッダに必要なものをセットする    
    $req->header( 'Content-type' => 'text/xml', 
                     'Content-length' => length($xml) );
    $req->content( $xml );
    
    # リクエスト!
    $resp = $ua->request( $req );

    if( $resp->is_success() ) { 
	$ret = $resp->content;
	$text = $ret;
	# 結果表示は手を抜いて、タグを削除するだけ
	$text =~ s/<[^>]+>/ /g;
	$text =~ s/[ ]+/ : /g;
	if( $ret =~ m|<methodResponse>| ) {
	    # XML-RPCのレスポンスが返っている
	    if( $ret =~ m|<fault>| ) {
	        # エラーが発生している
		return "Ping FAILED: $server RETURNS $text<br>\n";
	    }
	    # 多分成功している
	    return "Ping SUCCESS: $server: RETURNS $text<br>\n";
	} else {
	    # XML ではない結果のようだ
	    return "Ping FAILED: $server: RETURNS $ret<br>\n";
	}
    } else {
        # 接続に失敗しているか、リクエストで送ったXMLが正当でない
	return "Ping FAILED: $server: " . $resp->code() . "<br>\n";
    }
}

Pingサーバの機能

で、実際筆者は http://ping.bloggers.jp/rpc でやっている。ここは「ひらた だいじ」氏が運営する日本の Ping サーバでは古手のサイトだ。まあ、ここのabout.html や api.htmlを読むと、どんなことができるのか?が判る。URLを指定して「そのURLの更新時間を取得する!」なんてことも簡単だ。

http://ping.bloggers.jp/update?format=xml&url=URL指定

で、結果が取れるわけである。あと、こういうサービスの場合、changes.xml で、最近登録されたリストを取得することが出来たりするわけである。まあここらへん「RSSによる新着情報の配布について」から見ることが出来るようにしてある。

こんなところで Ping 自動送信なんかも可能になるわけである。結構単純な仕掛けなのが、今風のような気がするな。まあこれで、一応「クラシックなHTMLサイト」のクセに、「ブログ風の最新RSSサポートのあるサイト」になったんではないか?などと思わずウヌボレるわけだ(←馬鹿だから笑うこと!)。



copyright by K.Sugiura, 1996-2006