Perl Hackers Hub

第4回Twitterから学ぶ、Web APIのキホン(2)

リクエストURIを構築する

URIエスケープ

いよいよPerlを使ってWeb APIを利用していくわけですが、その前にリクエストURIを作る際の約束事を確認しましょう。

URIの仕様では、ASCIIAmerican Standard Code for Information Interchange文字以外の文字、つまり日本語などはURIで使用できないと定められています。URIで日本語を使いたい場合は、URIエスケープを行う必要があります。

たとえばTwitter Search APIで「おはよう」をキーワードに検索したい場合、

http://search.twitter.com/search.json?q=おはよう

と日本語を直接入れてはいけません。次のようにURIエスケープする必要があります。

http://search.twitter.com/search.json?q=%E3%81%8A%E3%
81%AF%E3%82%88%E3%81%86

Perlでクエリの名前と値の連結や、URIエスケープを行う際には、URIモジュールを使います。

モジュールのインストール

URIモジュールの使い方の説明の前に、モジュールのインストール方法について簡単に説明します。

これからURIモジュールを含めいくつかのモジュールを解説していきますが、モジュールがあなたの環境に入っていない場合があります。そのようなときには次のようなエラーメッセージが出力されます。

Can't locate URI.pm in @INC ...

モジュールのインストールは、CPANシェルもしくは本連載第1回を担当した宮川達彦さんが開発中のApp::cpanminusを使うのが一般的です。

CPANシェルの場合、CPANシェルを起動してから、

cpan > install URI

とすればインストールが行われます。

App::cpanminusでは、コンソールから直接

$ cpanm URI

とすればインストールが行われます。

モジュールをどこの場所にインストールするかはいろいろと議論があるので、各自でWeb上の情報を参考にしてください。ちなみに筆者は普段、cpanmコマンドに--sudoオプションを付けて実行し、root権限で開発マシン全体にモジュールをインストールしちゃっています。

URIモジュールを使う

では、perldocのSYNOPOSIS[3]風味にURIモジュールの使い方を説明します。

Twitter Search APIから「おはよう」という検索キーワードの人気ツイートを取得するためのURIを、単純に標準出力するコードは以下です。

use URI;
use utf8;

my $query = 'おはよう';
my $uri =
    URI->new('http://search.twitter.com/search.json');
my $params = {
    q => $query,
    result_type => 'popular',
};
$uri->query_form(%$params);
print "$uri\n";

result_typeはTwitter Search APIのオプションクエリパラメータで、recent(最近のツイート⁠⁠、popular(人気のツイート⁠⁠、mixed(最近と人気の結果どちらも含むツイート)を指定できます。今回はpopularを指定しました。

出力は次のようになります。

$ perl uri.pl
http://search.twitter.com/search.json?result_type=popu
lar&q=%E3%81%8A%E3%81%AF%E3%82%88%E3%81%86

query_formメソッドにクエリの名前と値のペアの組みを持つハッシュを渡すと、URI文字列における「?result_type=popular&q=(略⁠⁠」の部分へと変換をしてくれます。URIエスケープもその際に行ってくれます。

URI::Escapeモジュール

ちなみに、文字列連結だけでURIを構築するケースなど、単純に文字列をURIエスケープしたいだけの場合は、URI::Escapeモジュールを使うとよいでしょう。

use URI::Escape qw( uri_escape_utf8 );
use utf8;

my $qeury = 'おはよう';
print uri_escape_utf8( $query ) . "\n";

上記では、uri_escape_utf8メソッドをインポートして利用し、utf8フラグの付いた通常の日本語をURIエスケープ文字列へと変換しています。

Web APIにリクエストする

URIモジュールによって作成したURI(正確にはURI文字列やURIオブジェクト)にGETリクエストを行い、レスポンスを受け取ってみましょう。

LWP::UserAgentモジュール

今回はLWP::UserAgentモジュールを使いWeb APIへアクセスしてみます。LWP::UserAgentはWeb APIへのアクセスのみならず、PerlでHTTP通信を行ううえでよく使われるモジュールなので、基本的な使い方は覚えておいたほうがよいでしょう。

LWP::UserAgentことはじめ

Webページのタイトルを取得するコード

Twitter Search APIへアクセスしたい気持ちを抑えつつ、まずは最小限の例としてgihyo.jpのHTMLを単純に取得し、タイトルだけを出力する例を見ていきましょう。

use URI;
use LWP::UserAgent;

my $uri = URI->new('http://gihyo.jp/');
my $ua = LWP::UserAgent->new();  …(1)
my $res = $ua->get( $uri );  …(2)
die $res->status_line if $res->is_error;  …(3)
my ($title) = $res->content
    =~ m!<title>(.+?)</title>!i;  …(4)
print "$title\n";

このスクリプトを実行すると、現在のgihyo.jpのタイトルが標準出力に出力されるはずです。以降で要所を解説していきます。

GETメソッドによるリクエスト/レスポンス処理の基本

(1)でオプションを指定せずにLWP::UserAgentオブジェクトを作成しています。もしユーザエージェントを「wpcrawler⁠⁠、タイムアウトを30秒にしたければ、次のようにnewメソッドへハッシュを渡してあげるとよいでしょう。

my $ua = LWP::UserAgent->new( agent => 'wpcrawler',
                                       timeout => 30 );

getメソッドにURI文字列やURIオブジェクトを渡すと、実際にそのURIへGETリクエストが発生します。そして返り値としてHTTP::Responseオブジェクトが返ってきますので、それを(2)のようにして変数へ代入しておきましょう。

ここで出てきたHTTP::Responseはその名のとおりHTTPのレスポンスを汎用的に扱えるような一般的なモジュールです。もちろんHTTP::Requestというモジュールもあり、上記のgetメソッドでは内部的にそれを利用しています。

HTTP::Responseオブジェクトのis_successやis_errorメソッドは、そのHTTPレスポンスのステータスコードを見て条件分岐するのに使えます。is_successメソッドではそのステータスコードが200番台であれば真を、is_errorメソッドではそのステータスコードが400番台もしくは500番台であれば真を返します。

また、status_lineメソッドではステータスコードそのメッセージという文字列を出力します。たとえば200 OK404 Not Foundといった具合です。ですので(3)の部分は、もしレスポンスがエラーを返したら、ステータスコードとそのメッセージを出力しながら終了させるという意味です。

もしレスポンスがエラーを返さなかった場合には、(4)でそのレスポンスのコンテンツ、つまり実際のHTML文字列自体を取得して、正規表現でタイトルを切り取っています。

ここまでがLWP::UserAgentの最低限の使い方ですが、この程度を覚えておけば多くのWeb APIとのやりとりに対応できると思います。Twitter Search APIを利用する場合、(4)のレスポンスのコンテンツがJSONフォーマットになり、パースすることで取得結果を利用していくことになります。

結果をパースする

Twitter Search APIのレスポンスは、JSONとXML(Atom)で提供されています。今回はJSONをパースしてツイートを解析してみます。

JSONモジュール

JSONフォーマットを解析するにはJSON::XSモジュールやJSON::PPモジュールを使うのが一般的です。今回紹介するのはJSONモジュールなのですが、実際のところcpanシェルやcpanmコマンドなどで「JSON」と指定してインストールすると、JSON::XSがインストールできればそれが、もしできない場合はJSON::PPが入るという具合です。JSONモジュール自体がその2つのインタフェースを吸収する形になっているので、今回紹介する使い方を覚えればどちらが入っていようが特に問題ないはずです。

JSONテキストからの変換

ここでは、次の名前と値のペアのハッシュ構造を持つJSONを例に取ります。

  • user:yusukebe
  • message:おはよう

JSONにおける日本語文字列は、\uXXXX」という形式のUnicodeベースのASCII文字にエスケープがかけられていることが多いです。Twitter Search APIで取得できるJSONもそのようなものです。たとえば「おはよう」\u304a\u306f\u3088\u3046」にエスケープされています。

以下は、JSONメッセージをPerlのデータ構造に変換してから出力するスクリプトです。

use JSON qw( decode_json );
use Encode qw( encode_utf8 );

my $json_text =
'{"user":"yusukebe","message":"\u304a\u306f\u3088\u3046"}';
my $ref = decode_json($json_text);
print encode_utf8("$ref->{user} : $ref->{message}\n");

JSONモジュールのdecode_jsonメソッドをインポートして利用しています。このメソッドの返り値がPerlのハッシュリファレンス構造になっているので、そのままutf8フラグを落としつつ、printしています。

# 実行結果
yusukebe : おはよう

JSONテキストへの変換

ちなみに、Perlのデータ構造からJSONテキストへの変換もJSONモジュールで行えます。

use JSON;
use utf8;

my $json = JSON->new;  …(1)
$json->ascii(1);  …(2)
my $json_text =
$json->encode({ user => 'yusukebe', message => "おはよう" });
print "$json_text\n";

(1)のように、JSONモジュールはOOObject Orientedインタフェースにも対応しています。OOインタフェースの利用について簡単に補足すると、何度もencode/decodeメソッド呼び出す場合、一度JSONモジュールをnewメソッドでオブジェクト化しておくと、関数をインポートするより高速になります。

今回は先述した日本語などの\uXXXX」へのエスケープを有効にするために(2)としています。あとはJSONオブジェクトのencodeメソッドでPerlのハッシュリファレンスを渡してあげると、JSON形式のテキストに変換してくれます。

これでJSONのdecode/encodeができるようになりましたね。

# 実行結果
{"user":"yusukebe","message":"\u304a\u306f\u3088\u3046"}

おすすめ記事

記事・ニュース一覧