第2回 RSSフィードの料理はLWPとXML::RSSにおまかせ作って学ぶ、今どきのWebサービス(2/2 ページ)

» 2007年02月22日 08時00分 公開
[はてな 伊藤直也,ITmedia]
前のページへ 1|2       

HTTPの条件付きGETって?

 さて、モジュールの使い方に慣れてきたところでもう少し踏み込んだプログラミングをしていきましょう。

 LWP::Simpleのgetメソッドでは、与えられたURLのコンテンツをHTTPのGETで取得します。一度コンテンツを取得するだけならそれでも構わないのですが、例えばスクリプトを定期的に実行する場合などは、特定のURLに対して何度もGETを発行することになります。このとき、何度も同じコンテンツを取得しにいくよりは、

  • 相手のサイトが更新されていた場合のみGET
  • それ以外のときは以前のコンテンツをローカルに保存しておいてそちらを参照する

といったことができれば、相手のサイトにかかる負荷が減り、効率が良いですね。

 この仕組みはHTTPプロトコルでサポートされていて、「If-Modified-Sinceヘッダによる条件付きGET」なんて呼ばれたりします。

  • サーバ側は、コンテンツがGETされたときにコンテンツと一緒にLast-Modifiedヘッダを送信する
  • クライアント側はHTTPヘッダのIf-Modified-Sinceで、前回取得したときにサーバから送信されたLast-Modifiedヘッダを送信する
  • サーバ側は、If-Modified-Sinceとコンテンツの更新時間を比較して、更新されていなかったら304 Not Modifiedとヘッダのみ、更新されて至ら200 OKとコンテンツすべてを返す

 これによって、サーバ側は同じクライアントには一度だけしか文書を送信しなくて良いので負荷が減るといった仕組みです要するに同じサイトに何度もコンテンツを取得しにいく場合はIf-Modified-Sinceヘッダを使うのがマナーですよ、ということです。

LWPで条件付きGET

 さて、この条件付きGETを使ってコンテンツをGETしてくる方法を考えます。自分でHTTPヘッダに時刻を挿入してローカルにキャッシュして……とか考えるとちょっと面倒臭いですね。しかし、そこはモジュールさまさま。LWPにはこの条件付きGETを行うメソッドもしっかり用意されています。

 LWP::Simpleのmirrorメソッドがそれです(リスト4)。第1引数がURL、第2引数が取得したコンテンツの保存先です。先のスクリプトをさらに改造して、条件付きGETをするように変更してみましょう(リスト5)


mirror('http://www.example.com/', '/path/to/cache');

リスト4 mirrorメソッド


1 #!/usr/local/bin/perl
2 use strict;
3 use Digest::MD5 qw(md5_hex);
4 use Encode;
5 use LWP::Simple;
6 use XML::RSS;
7
8 our $CacheDir = '/tmp/rsscache';
9 if (! -e $CacheDir) {
10 mkdir $CacheDir or die "cannot create $CacheDir: $!";
11 }
12
13 my $url = shift;
14 my $cache = sprintf("%s/%s.xml", $CacheDir, Digest::MD5::md5_hex($url));
15
16 LWP::Simple::mirror($url, $cache)
17 or die "cannot get content from $url";
18
19 my $rss = XML::RSS->new;
20 $rss->parsefile($cache);
21 for (@{$rss->{items}}) {
22 print encode('euc-jp', $_->{title}), "\n";
23 }

リスト5 LWPで条件付きGET

 まずは3行目。後ほどURLをMD5形式にするため、MD5を使用します。MD5を利用できるよ

うにするモジュールDigest::MD5をuseでロードしておきます。

 8〜11行目では、ローカルキャッシュの保存先ディレクトリがなかったら作成しています。14行目では、キャッシュファイルのファイル名を作っています。ファイル名は何でも良いので、ここでは取得する対象のURLからユニークな文字列をMD5で作り、それをファイル名にしています。Digest::MD5モジュールのmd5_hex関数に文字列を渡せば、そのMD5が返ってきます。

 16、17行目。いままではgetだったのをmirrorメソッドに変更しました。20行目では、スカラーデータ*をparseに渡していたところを、ローカルに保存したキャッシュに変更しています。このスクリプトを実行すると、先ほどと出力結果はまったく一緒ですが、Webサイトとの通信は最小限にとどめてローカルキャッシュを活用して動作するようになります。仕組みを理解するのがちょっと面倒臭いですが、実装はモジュールのおかげで簡単ですね。

より細かなHTTPクライアントはLWP::UserAgentで

 LWP::Simpleはその名前に「Simple」とあるように、シンプルなHTTPクライアント用のモジュールです。もっと細かくさまざまなパラメータを制御したい場合や、オブジェクト指向プログラミングに役立てたいといった場合には、LWP::UserAgentを使います。

 例えば、User-Agentやタイムアウトの時間を任意の値に設定し、Proxyサーバを通してHTTP GETを行うには、リスト6のようなコードになります。以下、簡単に解説していきましょう。


1 #!/usr/local/bin/perl
2 use strict;
3 use LWP::UserAgent;
4 use HTTP::Request;
5
6 my $request = HTTP::Request->new( GET => 'http://d.hatena.ne.jp/naoya/rss');
7
8 my $ua = LWP::UserAgent->new;
9 $ua->timeout(10);
10 $ua->proxy(['http', 'ftp'], 'http://proxy.example.com:3128/');
11 $ua->agent('UNIX USER Sample Client/0.01');
12
13 my $response = $ua->request($request);
14 if ($response->is_success) {
15 print $response->content;
16 } else {
17 die sprintf("error(%d): %s", $response->code, $response->message);
18 }

リスト6 LWP::UserAgentで作ったHTTPクライアント

 まず6行目です。LWP::UserAgentでは、リクエストを送るための方法が幾つか用意されていますが、ここではHTTP::Requestを使った方法で実装しています。まずHTTP::Requestでリクエストのインスタンスを作り、それをLWP::UserAgentのインスタンスに渡す、という手順です。HTTP::Requestでは、ほかにもリクエストヘッダをカスタマイズしたりといったことも可能です。

 8〜10行目では、LWP::UserAgentのインスタンスを生成して、そこにタイムアウト値などのパラメータを設定しています。ほかにもCookieの値やリダイレクトを処理するかどうかなど幾つかの値が設定可能です。

 最後に13〜18行目です。6行目で作ったHTTP::Requestインスタンスを指定してLWP::UserAgentでリクエストを行うと、返却値としてレスポンスのオブジェクト(HTTP::Responseのインスタンス)が返ってきます。このオブジェクトに、リクエストが成功したかどうかを尋ねて、成功なら結果を出力、失敗ならエラー内容を、HTTPステータスコードとメッセージとともに出力しています。

 動作を細かく制御したい場合は、以上のように、

リクエスト              HTTP::Request

HTTPクライアント        LWP::UserAgent

レスポンス              HTTP::Response


という3つのクラスを使って、プログラミングしていくことになります。

そのほかのHTTPクライアントモジュール

 CPANに登録されているHTTPクライアントモジュールにはさまざまなものがありますが、LWP::SimpleやLWP::UserAgent以外にも次に挙げるモジュールが定番です(いずれもLWP::UserAgentのラッパーモジュール*です)。

  • LWP::RobotUA:well-behavedなWebロボット*として振る舞うクライアントを書くためのモジュール(robot.txtなどを解釈して過剰なアクセスを行わないようなクライアントを簡単に書ける)
  • WWW::Mechanize:リンクをクリックして、1つのサイトの中を遷移していった結果を取得するような場合に利用するモジュール。フォーム認証付きのページの奥にあるコンテンツや、セッションで管理されているページのコンテンツを取得するのに役立つ

次回は

 ここまでで、XML::RSSによるRSSの料理方法を解説しました。ではRSS以外のXML文書を料理する場合にはどうしたら良いのでしょう? 次回はそのあたりについて解説します。

このページで出てきた専門用語

スカラーデータ

構造を持たないプレーンなデータのこと。ここでは文字列データを指している。

LWP::UserAgentのラッパーモジュール

LWP::UserAgentモジュールを内部的に使用して、必要な機能のみ提供するモジュール。ラップは「くるむ」の意味。

well-behavedなWebロボット

行儀の良いWebロボットという意味。Webを巡回するために作られたソフトウェアでは、robots.txtを確認して、Web管理者の意図しないファイルにアクセスしないようにしなければいけない。Webロボットの中にはrobots.txtを無視するものもあるが、それは行儀が悪いとされている。


本記事は、オープンソースマガジン2005年7月号「作って学ぶ、今どきのWebサービス 第1回」を再構成したものです。


前のページへ 1|2       

Copyright © ITmedia, Inc. All Rights Reserved.