はじめに
「Web 2.0」という言葉にソワソワしているアナタ。そう、そこのアナタです。どうっすか? coolなcode書いてますか? 短めの茶髪をツンツン立ててますか? 流行のメガネ男子をやってますか? あ、さっそく脱線してますね。すいません。
「Web 2.0」という言葉には、明確な定義がないため、非常にあいまいな使われ方をしています。なんとなく「Web 2.0」っぽいとか言われても、人によって基準が違うので、概念を共有できる保障がありません。言葉だけが一人歩きしてしまったために、バズワードだという識者もいます。
「Web 2.0」的と言われているサービスでは、新しい技術によって何かを成しているというよりも、既存の技術の捉え方を変えたり、組み合わせたりして、新しいサービスを実現していることがほとんどです。「Web 2.0」を新しい技術だと捉えると、本質を見失ってしまう危険性があります。
本稿では、「Web 2.0」的アプローチの1つ、APIの公開されたサービスを繋ぎ合わせて新しいサービスを作る「マッシュアップ」について、決して新しい技術ではないことを示し、技術ではなく何が新しいのかを考えてみます。
対象読者
- 「Web 2.0」がよく分からなくって、焦ったり、ヘコんだりしている方。
- 「Web 2.0」という言葉に煽られて、自分を見失っている方。とにかく「2.0」って言ってれば、何とかなると思っている方。
- RSSに対応していないWebサイトをウォッチしていて、手動で巡回するのが時間の無駄に思えてきた方(手動で時間の無駄バンザイな方は対象ではありません)。
- いつも利用させてもらっているサービスに感謝し、サーバの負荷にも気を使える、謙虚な方。
必要な環境
コードの動作確認は、以下の環境で行っています。
- CentOS 4.3
- PHP 4.3.9
- PEAR 1.4.9
- HTTP_Request 1.3.0
- Cache_Lite 1.7.2
Windows上でも、おそらくは動作するでしょう。
PEARモジュールである、HTTP_Request、Cache_Liteは、明示的にインストールしなければ使えませんので、注意してください。また、PEARモジュールを更新する際に、「php.ini」のメモリ使用量設定を増やす必要があったり、直接最新版に更新できなかったりするかも知れません。詳しいことは、ググってください。
「マッシュアップ」とは
「マッシュアップ」とは、複数のサービスAPIを組み合わせて、新しいサービスを提供することです。提供されるサービスは、Webブラウザから利用できたり、さらに別のマッシュアップに使ってもらえるように、サービスAPIを公開したりします。ここで言う「サービスAPI」とは、Webサイトなどで提供されているサービスの機能を、HTTP経由で外部から呼び出せるように公開したものです。サービスAPIの呼び出しには、SOAPやRESTの枠組みが用いられ、結果は、XML形式で得られることが一般的です。サービスAPIの多くは、無料であっても登録しないと使えない形で提供されています。
例えば、人名辞典を提供しているWebサイトと、イメージ検索を提供しているWebサイトが、サービスAPIを公開している場合、人名を取得して、その人名でイメージ検索をかける機能を持ったアプリケーションを作成できることになります。一般的に、こうしたアプリケーションは、デスクトップで動くものではなく、サーバに実装されて、Webブラウザを通じて利用する形で提供されます。
「マッシュアップ」は「Web 2.0」か?
「Web 2.0」の具象として引き合いに出される「マッシュアップ」ですが、これは、サービスAPIの利用による新しいサービスの構築という側面を指してのことだと考えられます。
しかし「マッシュアップ」は、サービスAPIが無くても構築することが可能です。Webブラウザを経由してサービスを使用できるWebサイトであれば、HTTP経由でWebページを取得するプログラムを書くことで、人間ではなくプログラムがサービスを受けられます。一般的にこうしたWebサイトは、URL引数もしくはPOSTデータによってリクエストし、HTML文書の形式でレスポンスを得ています。人間のWebブラウザの操作で行われるリクエストと同じものをプログラムが投げ、Webブラウザがレンダリングして人間に見せるレスポンスをプログラムが受け取り、解釈して利用すれば、サービスAPIを利用するのと同じ結果が得られます。
この「Web 2.0」的でない「マッシュアップ」を、ここでは「Web 1.5的マッシュアップ」と呼ぶことにしましょう。
イラスト検索
「Web 1.5的マッシュアップ」の例として、「イラスト検索」を作ってみました。
<?php // イラスト検索 by mnagaku @ 2006/06/23 // このファイルはUTF8で書かれています。サーバの設定によっては変更が必要です header('Content-type: text/html;charset=UTF-8'); echo '<html><head><title>イラスト検索</title></head><body>'; // keywordが取得できなかったら、入力を求める if(!isset($_GET['keyword']) || strlen($_GET['keyword']) <= 0) view_index("キーワード"); // tinamiに接続するURLの生成 $tinami_url = "http://www.tinami.com/search/result.php?disp=50&word=" .urlencode(mb_convert_encoding($_GET['keyword'], "EUC-JP", "auto")); // キャッシュの準備(pearモジュールが必要) require_once 'Cache/Lite.php'; $web_cache =& new Cache_Lite(array('automaticCleaningFactor' => 100)); // キャッシュを調べて、あったら使う if($content = $web_cache->get($tinami_url)) { view_index("別のキーワードで検索する?", FALSE); echo urldecode($content); exit; } // IE対策(256bytes溜まるまでレンダリングされないので) for($i = 0; $i < 3; $i++) echo ' '; // 検索中 echo '<div id="head">検索中です。しばらく、お待ちください</div>'; ob_flush(); flush(); // httpクライアントの準備(pearモジュールが必要) require_once 'HTTP/Request.php'; $http_client =& new HTTP_Request(); // tinamiに接続 $http_client->setURL($tinami_url); if(PEAR::isError($http_client->sendRequest())) { echo '<script language="JavaScript">document.getElementById("head")' .'.innerHTML="TINAMIへの接続が行えませんでした";</script>'; view_index("キーワード"); } // tinamiへの問い合わせ結果から、ググるサイトのURLを取得 $tinami_result = array(); preg_match_all( "/sitename\"><a href=\"\/cgi-bin\/launcher\?key=(.*)&URL=(.*)\" target/iU", $http_client->getResponseBody(), $tinami_result); // tinamiへの問い合わせ結果から、ググるサイトのタイトルを取得 $title_result = array(); preg_match_all( "/<A HREF=\"\/cgi-bin\/launcher\?key=(.*)&URL=(.*)\">(.*)<\/A><!--IMG/iU", $http_client->getResponseBody(), $title_result); // tinamiで取得したサイトごとに、画像をググる $exec_time = strtotime("now"); $content = "<p>※".date("Y-m-d H:i")."に作られたキャッシュから復元した内容です<p>"; $i = 0; foreach($tinami_result[2] as $site) { // googleに接続 $http_client->setURL("http://images.google.co.jp/images?q=".$site); if(PEAR::isError($http_client->sendRequest())) { echo '<script language="JavaScript">document.getElementById("head")' .'.innerHTML="googleへの接続が行えませんでした";</script>'; view_index("キーワード"); } // googleへの問い合わせ結果から、画像のURLを取得 $google_result = array(); preg_match_all("/<a href=\/imgres\?imgurl=(.*)&imgrefurl=/iU", $http_client->getResponseBody(), $google_result); // 画像の存在を確認し、あったものだけ表示対象とする $view_flag = FALSE; $graph_list = ""; foreach($google_result[1] as $graph) { $http_client->setURL($graph); if(!PEAR::isError($http_client->sendRequest()) && $http_client->getResponseCode() / 100 == 2) { $graph_list .= "<a href=\"".$graph."\"><img src=\"".$graph ."\" width=30></a>"; $view_flag = TRUE; } } // 画像の見つかったサイトへのリンクを生成 if($view_flag) { $sitename = "<br><a href=\"".urldecode($site)."\">" .mb_convert_encoding($title_result[3][$i],"UTF-8","EUC-JP")."</a>"; $content .= $sitename.$graph_list; echo $sitename.$graph_list; } $i++; echo '<script language="JavaScript">document.getElementById("head")' .".innerHTML='検索中です。しばらく、お待ちください:" .floor($i / count($tinami_result[2]) * 100)."%';</script>"; ob_flush(); flush(); } // 検索中の表示を、検索窓に変える $exec_keyword = '<p>検索したキーワード「' .mb_convert_encoding($_GET['keyword'], "UTF-8", "auto") .'」<p>検索に要した時間:'.(strtotime("now") - $exec_time).'秒'; $content .= $exec_keyword; echo $exec_keyword; echo '<script language="JavaScript">document.getElementById("head")' .".innerHTML='"; view_index("別のキーワードで検索する?", FALSE); echo "';</script></body><html>"; // キャッシュに保存 $web_cache->save(urlencode($content.'</body><html>'), $tinami_url); // 初期画面の表示 function view_index($label, $end = TRUE) { echo '<form action="tinami.php" method="get">'.$label.':<input type='; echo '"text" name="keyword"><input type="submit" value="検索"></form>'; if($end) { echo '</body><html>'; exit; } } ?>
このPHPスクリプトを、PEARモジュール、HTTP_Request、Cache_Liteが動作するサーバに配備して、アクセスしてみてください。このプログラムは、アニメ・マンガ系クリエイターの総合情報サイト「TINAMI」に登録された約3万のWebサイトから、与えられたキーワードにマッチするWebサイトのURLを入手し、そのWebサイトについて、グーグルイメージ検索を実施します。このプログラムを利用することで、キーワードに関連したワークのあるアニメ・マンガ系クリエイターの作品を調べることができます。