リクエストURIを構築する
URIエスケープ
いよいよPerlを使ってWeb APIを利用していくわけですが、その前にリクエストURIを作る際の約束事を確認しましょう。
URIの仕様では、ASCII(American 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();
my $res = $ua->get( $uri );
die $res->status_line if $res->is_error;
my ($title) = $res->content
=~ m!<title>(.+?)</title>!i;
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 OK
」「 404 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;
$json->ascii(1);
my $json_text =
$json->encode({ user => 'yusukebe', message => "おはよう" });
print "$json_text\n";
(1) のように、JSONモジュールはOO(Object Oriented )インタフェースにも対応しています。OOインタフェースの利用について簡単に補足すると、何度もencode/decodeメソッド呼び出す場合、一度JSONモジュールをnewメソッドでオブジェクト化しておくと、関数をインポートするより高速になります。
今回は先述した日本語などの「\ uXXXX」へのエスケープを有効にするために(2) としています。あとはJSONオブジェクトのencodeメソッドでPerlのハッシュリファレンスを渡してあげると、JSON形式のテキストに変換してくれます。
これでJSONのdecode/encodeができるようになりましたね。
# 実行結果
{"user":"yusukebe","message":"\u304a\u306f\u3088\u3046"}