ゆーすけべー日記

はてなBlogってどーなの!?

POX over HTTP のウェブAPIにアクセスするためのモジュール「WebService::Simple」を作ってみた

俗に言う「REST/XML over HTTP POX over HTTP」のウェブAPIにアクセスするためのシンプルな(?)Perlモジュール「WebService::Simple」なるものを作ってみました。REST/XML over HTTP POX over HTTP というのは、俺の解釈でいいますと、パラメータを指定して GET (POSTの場合もある)により情報の入ったコンテンツをXML形式で取得するというものです。WebService::Simple は基本的にこれ従っていればどんな API にも対応しているはずです。もしくはそうしたいです。

追記: masakiさんからTwitter経由で突っ込みをもらったので修正しておきました。確かにRESTという言葉が入っていると誤解されちゃいますよね>< 参考: POX over HTTP - naoyaのはてなダイアリー

@yusukebe あれで REST とか言うと誤解を生むから POX over HTTP と言って欲しい

最近こうしたウェブAPIを利用するためのプログラムをいくつか作っているのですが、何回も同じ処理をコピペしているのに気づいて、短いコードながらも(50行くらいだよ)モジュール化してみたという経緯です。 ウェブAPI を利用するには、

  1. リクエストURLの作成
  2. コンテンツの取得
  3. XMLのパース

という3ステップがあるので、この処理を「WebService::Simple」に任せるようにしました。 XMLのパースには個人的に扱いやすいと思っている XML::Simple を使っているため、返却される値はPerlオブジェクトのリファレンスとなります。

例としてFlickrのAPIを挙げます。キーワードで写真を検索して結果をダンプするスクリプトは、WebService::Simple を使わないとしたら以下のように書けます。

#!/usr/bin/perl

use strict;
use warnings;
use LWP::Simple;
use XML::Simple;
use URI::Escape;
use YAML;
use utf8;
binmode STDOUT, ":utf8";

my $api_key = "your_api_key";
my $url     = make_url( "flickr.photos.search", { text => "猫" } );
my $content = get($url) or die "Couldn't get xml";
my $xs      = XML::Simple->new( forcearray => ["photo"], keyattr => [] );
my $results = $xs->XMLin($content);
print Dump $results;

sub make_url {
    my ( $method, $param ) = @_;
    my $url = "http://api.flickr.com/services/rest/?";
    $url .= "method=$method&api_key=$api_key";
    foreach my $key ( keys(%$param) ) {
        $url .= "&$key=" . URI::Escape::uri_escape_utf8( $param->{$key} ) . "&";
    }
    return $url;
}

上記した3つのステップがコード内に入っています。では次に、WebService::Simpleを使ったサンプルコードはこちらとなります。

#!/usr/bin/perl

use strict;
use warnings;
use WebService::Simple;
use YAML;
use utf8;
binmode STDOUT, ":utf8";

my $api_key  = "your_api_key";
my $base_url = "http://api.flickr.com/services/rest/";
my $flickr = WebService::Simple->new( $base_url, { api_key => $api_key } );
my $response =
  $flickr->get( { method => "flickr.photos.search", text => "猫" } );
my $ref = $response->parse_xml( { forcearray => ["photo"], keyattr => [] } );
print Dump $ref;

リクエストURLを作るのとXML::Simpleのインスタンスを作成してパースという処理が無くなったためコードがすっきりしました。また、パラメータをハッシュリファレンスで指定するので、いろいろな種類のリクエストを作るのが楽です。WebService::Simple のメソッドは以下の通りです。

new
第一引数にAPIのベースURL、 第二引数にアプリケーションIDなどそのAPIを使うにあたって常に必要になってくるパラメータを指定
get
引数にはパラメータと値が格納されたハッシュリファレンスを渡す。 HTTP::Responseオブジェクトにparse_xmlというメソッドを追加した WebService::Simple::Response オブジェクトが返ってくる

getメソッドで返される WebService::Simple::Response オブジェクトの parse_xml のパラメータには、XML::Simple の new で指定するものをハッシュリファレンスで渡してあげます。するとXML::SimpleでXMLコンテンツをパースしたリファレンスが返ってきます。

もちろん Flickr 以外のAPIのアクセスにも使えて、Amazon で書籍検索するサンプルはこんな感じです。

#!/usr/bin/perl

use strict;
use warnings;
use WebService::Simple;
use YAML;
use utf8;
binmode STDOUT, ":utf8";

my $access_key    = "your_access_key";
my $associate_tag = "your_associate_tag";
my $base_url      = "http://webservices.amazon.co.jp/onca/xml";
my $param         = {
    Operation     => "ItemSearch",
    SearchIndex   => "Books",
    Keywords      => "perl",
    ResponseGroup => "Small,Images",
};

my $amazon = WebService::Simple->new(
    $base_url,
    {
        Service        => "AWSECommerceService",
        SubscriptionId => $access_key,
        AssociateTag   => $associate_tag,
    }
);
my $response = $amazon->get($param);
my $ref =
  $response->parse_xml( { forcearray => [ "item", "Author" ], keyattr => [] } );
print Dump $ref;

ようは、ウェブAPIを使う時に、リクエストURLをいちいち作るのめんどくせーよ、返ってきたXMLは(処理が遅くてもいいから扱いやすい)XML::Simpleで返してよ!という超個人的な要望を叶えるモジュールです。 今後欲しい機能として、API のアプリケーションIDなどを YAML で設定ファイルとして持たせて、それを自動的に読んだりしたら楽チンだなと思っています。 例によって CodeRepos で管理することにしましたので興味がある人は覗いてください。 そして突っ込み、添削大歓迎です。