PHPで「Yahoo! ウェブ検索Webサービス」を利用する

(1/1)
XML の扱い方が分かったところで、実際に「Yahoo! ウェブ検索Web サービス」を利用した検索プログラムを作成してみる。

※この WebAPI は 2013 年(平成 25 年)3 月 31 日に停止したため、ここで紹介するプログラムは動作しない。

アプリケーションID

Yahoo! ウェブ検索Web サービス」を利用するためには、アプリケーション ID を取得する必要がある。http://e.developer.yahoo.co.jp/webservices/register_application から無料で取得できる。

サンプル・プログラム

サンプル・プログラムの解説:前準備

サンプル・プログラムには 2 つの機能を持たせた。
1 つは、そのまま実行すると、検索キーワードを入力する画面を表示する機能である。[検索]ボタンを押下することで、検索結果を表示する。
もう 1 つは、以下の変数を GET 渡しで呼び出すことで、検索結果のみを表示することができる機能である。GET 渡しするパラメータは下記の通り。

query検索語必須
result検索結果表示数(0 以上 50 以下の整数)省略可能
site検索対象サイト(ドメイン名)省略可能
まず、取得したアプリケーション ID を変数 $ApplicationID に代入してほしい。

0018: /**
0019:  * Yahoo! JAPAN Webサービス アプリケーションID
0020:  *    アプリケーションIDは、
0021:  *    http://api.search.yahoo.co.jp/webservices/register_application
0022:  *    にて登録してください。
0023:  * @global string $ApplicationID
0024: */
0025: $ApplicationID = 'xxxxxxxxxxxxxxxxxxxxxx';
0026: 
0027: /**

リリース時には、サイバー攻撃を避けるため、PHP のシステム・エラーの出力抑止などを行うべく、変数 $Flag_release を用意した。デバッグ時は FALSE で、リリース時には TRUE に変更してほしい。

0027: /**
0028:  * リリース・フラグ
0029:  * @global bool $Flag_release TRUE/FALSE(公開時にはTRUEにすること)
0030: */
0031: $Flag_release = FALSE;

これにともない、自前のエラー処理ハンドラを用意した。

0033: /**
0034:  * エラー処理ハンドラ
0035: */
0036: function myErrorHandler ($errno$errmsg$filename$linenum$vars) {
0037:     echo "Sory, system error occured !";
0038:     exit(1);
0039: }
0040: error_reporting(E_ALL);
0041: if ($Flag_release)  $old_error_handler = set_error_handler("myErrorHandler");

サンプル・プログラムの解説:検索処理

ユーザー関数 getURL は、検索語などを「Yahoo! ウェブ検索Web サービス」に渡すための URL を返す。

2008 年(平成 20 年)8 月 12 日、Yahoo!検索プロジェクトが Yahoo!デベロッパーネットワークで提供している WebAPI のドメインが、yahoo.co.jp から yahooapis.jp に変更されましたことに伴い、プログラムを修正しました。⇒(Yahoo!検索、Yahoo!カテゴリの WebAPI ドメイン変更のお知らせ
クラウド連携を行う場合、突然、WebAPI が変更されることがあるので注意が必要です。

0069: /**
0070:  * Yahoo! ウェブ検索WebサービスAPI のURLを取得する
0071:  * @param string $query   検索語
0072:  * @param string $results 返却結果の数(1≦$result≦50)(省略時は10)
0073:  * @param string $start   返却結果の先頭位置
0074:  *                          ($start + $result - 1≦1000)(省略時は 1)
0075:  * @param string $site    検索ドメイン(省略時は '')
0076:  * @return string URL
0077: */
0078: function getURL($query$results$start$site) {
0079:     global $ApplicationID;
0080: 
0081:     $query = urlencode(sjis2utf8($query));       //検索語
0082: 
0083:     //定義域を逸脱した場合の処理
0084:     if ($results < 1)       $result = 10;
0085:     else if ($results > 50) $result = 50;
0086: 
0087:     if ($start + $results - 1 < 1)            $start = 1;
0088:     else if ($start + $results - 1 > 1000)    $start = 1000;
0089: 
0090:     $res = "http://search.yahooapis.jp/WebSearchService/V1/webSearch?appid="
0091:         . "$ApplicationID&query=$query&results=$results&start=$start&site=$site";
0092: 
0093:     return $res;
0094: }

前回のサンプル・プログラムとは異なり、<Title> だけでなく、以下の情報をすべて配列変数 $items に格納するため、ユーザー関数 getResults を用意した。

<Title>タイトル
<Summary>関連テキストサマリー
<Url>ページ URL
<ClickUrl>リンク URL
<MimeType>MIME タイプ
<ModificationDate>ページが最後に修正された日付(UNIX タイムスタンプ)
<Cache>キャシュ結果の URL;サイズは byte


getResult は一部の入力パラメータを省略可能にしているため、まず、その処理を行う。
次に、ユーザー関数 getURL を使って、検索URL を求める。

PHP4 では、この URL をユーザー関数 read_xml に渡し、検索結果(XML データ)を得る。read_xml は前回と同じである。

PHP4 では、この URL を関数  simplexml_load_file  に渡し、XML ファイルの読み込みと解釈を行う。

検索結果が正常に戻ってきたかどうかは、XML ファイルの <Result> 要素が存在しているかどうかでチェックを行う。
XML データから検索結果を取りだし、配列変数 $items に代入する。
あとは、表示処理に関する部分となる。

0124: /**
0125:  * Yahoo! ウェブ検索WebサービスAPIから必要な情報を配列に格納する
0126:  * @param array  $items   情報を格納する配列
0127:  * @param string $results 返却結果の数(1≦$result≦50)(省略時は10)
0128:  * @param string $start   返却結果の先頭位置
0129:  *                          ($start + $result - 1≦1000)(省略時は 1)
0130:  * @param string $site    検索ドメイン(省略時は '')
0131:  * @return int ヒット数
0132: */
0133: function getResults(&$items$query) {
0134: //受信データの要素名
0135: $tbl = array(
0136:     'Title',            //タイトル
0137:     'Summary',            //関連テキストサマリー
0138:     'Url',                //ページURL
0139:     'ClickUrl',            //リンクURL
0140:     'MimeType',            //MIMEタイプ
0141:     'ModificationDate',    // ページが最後に修正された日付(UNIXタイムスタンプ)
0142:     'Cache'             //キャシュ結果のURL。サイズはbyte。
0143: );
0144: 
0145:     //返却結果の数(省略可能)
0146:     if (func_num_args() >= 3) {
0147:         $results = func_get_arg(2);
0148:         if ($results < 1)       $results = 10;
0149:         else if ($results > 50) $results = 50;
0150:     } else {
0151:         $results = 10;
0152:     }
0153: 
0154:     //返却結果の先頭位置(省略可能)
0155:     if (func_num_args() >= 4) {
0156:         $start = func_get_arg(3);
0157:         if ($start + $results - 1 < 1)            $start = 1;
0158:         else if ($start + $results - 1 > 1000)    $start = 1000;
0159:     } else {
0160:         $start = 1;
0161:     }
0162: 
0163:     //検索ドメイン(省略可能)
0164:     if (func_num_args() >= 5) {
0165:         $site = func_get_arg(4);
0166:     } else {
0167:         $site = '';
0168:     }
0169: 
0170:     $url = getURL($query$results$start$site);      //リクエストURL
0171: 
0172: //PHP4用; DOM XML利用
0173:     if (isphp5() == FALSE) {
0174:         if (($dom = read_xml($url)) == NULL)    return FALSE;
0175:         $resultset = $dom->get_elements_by_tagname("ResultSet");
0176:         //レスポンス・チェック
0177:         $results = $resultset[0]->get_elements_by_tagname("Result");
0178:         if ($results == NULL)   return FALSE;
0179:         //検索結果取りだし
0180:         $cnt = 1;
0181:         foreach ($results as $element) {
0182:             foreach ($tbl as $name) {
0183:                 $node = $element->get_elements_by_tagname($name);
0184:                 if ($node != NULL) {
0185:                     $items[$cnt][$name] = utf82sjis($node[0]->get_content());
0186:                 }
0187:             }
0188:             $cnt++;
0189:         }
0190: 
0191: //PHP5用; SimpleXML利用
0192:     } else {
0193:         $ResultSet = simplexml_load_file($url);
0194:         //レスポンス・チェック
0195:         if (isset($ResultSet->Result) == FALSE)      return FALSE;
0196:         //検索結果取りだし
0197:         $cnt = 1;
0198:         foreach ($ResultSet->Result as $element) {
0199:             foreach ($tbl as $name) {
0200:                 if (isset($element->$name)) {
0201:                     $items[$cnt][$name] = utf82sjis($element->$name);
0202:                 }
0203:             }
0204:             $cnt++;
0205:         }
0206:     }
0207: 
0208:     return ($cnt - 1);
0209: }

質疑応答

【質問】 スキーパパ 様より

yahoosearch.php をダウンロードして自分の localhost 上で実行したのですが、下記の様なメッセージが出力されます。
Warning: fopen() [function.fopen]: php_network_getaddresses: getaddrinfo failed: そのようなホストは不明です。 in C:\Program Files\Apache Group\Apache2\htdocs\sanyo2\yahoosearch.php on line 92
Warning: fopen(http://api.search.yahoo.co.jp/WebSearchService/V1/webSearch?appid=yoshi_php&query=%E3%81%82&results=10&start=1&site=) [function.fopen]: failed to open stream: No error in C:\Program Files\Apache Group\Apache2\htdocs\sanyo2\yahoosearch.php on line 92


【回答】

Windows 上で PHP を動かしているようですが、ファイアウォールやブロードバンドルータの設定により、PHP の外部ネットへのアクセスが遮断されていないかどうか確認してください。
また、アプリケーション ID が間違っているようです。アプリケーション ID は二十数桁の英数字で、Yahoo! ID とは異なります。取得方法をご確認ください。(2007 年 11 月 18 日時点では、アプリケーション ID が不正でも検索できるようです。)

【質問】 スキーパパ 様より

またまたエラーが出てしまい...何かまだ設定が足りないのか考えています。
Notice: Undefined variable: response in C:\Program Files\Apache Group\Apache2\htdocs\sanyo2\yahoosearch.php on line 164
Fatal error: Call to a member function get_elements_by_tagname() on a non-object in C:\Program Files\Apache Group\Apache2\htdocs\sanyo2\yahoosearch.php on line 164


【回答】

最初のプログラムを掲載してから Yahoo! ウェブ検索Web サービス の API 仕様に変更があったようです。最初のプログラムでは、ご指摘のエラーが出てしまいます。
新しいプログラムをアップロードしてあります。そちらをご利用ください。

【質問】 スキーパパ 様より

私の PHP環境が Ver5.05 の状態でしたので、修正していただいたプログラムでもエラーで動きませんでした。他の環境で Apache2.0.61 PHP4.4.7 の環境下を作り導入しましたら、すんなり稼動しました。
domxml_open_mem関数が PHP5 では対応していないらしく、違う関数で考えたら動くのではと考えています。


【回答】

ご指摘ありがとうございます。このホームページを収容しているサーバの環境は PHP4.4.4 です。
PHP5 対応にあたっては、PHP4 から標準化された DOM Function での対応も考えたのですが、大幅に書き直す必要があります。もともと SimpleXML に移行するつもりでプログラムを書いていたので、分かりやすい SimpleXML の方を採用しました。
なお、PHP5 プログラムの動作検証は PHP 5.2.5 で行っています。

【質問】 ぱぴぃ 様より

yahoosearch.php の件について質問させてください。
日々の検索エンジンの順位チェックで使用させていただこうと思いますが、1 件 1 件の処理だと何の問題もなく動作するのですが、URL とキーワードをデータベースから抜き出して連続して数百件の処理を行おうとすると 50~100 件前後のところで PHP メモリオーバーのエラーが発生してとまってしまいます。何か回避策はありませんでしょうか?よろしくお願いいたします。


【回答】

具体的にどのような環境でどのようなスクリプトを走らせているのか分かりませんが、単純に PHP のメモリ不足による問題であれば、設定ファイル "php.ini" にあるmemory_limitの値を増やしてみてください。この値は、PHP プロセスに割り当てられる最大メモリ量をあらわします。

【質問】 ぱぴぃ 様より

ご回答ありがとうございました。
ご指摘の件につきましては、既に対応済みでして、.htaccess にて php_value memory_limit 512M と初期値 32M を大きく増やした状況で現状の症状となり、これ以上メモリ制限値を増やせないため質問させていただいた次第です。リミット値変更で対応出来なければアウトですかね・・・。


【回答】

どのようなスクリプトなのか分かりませんが、検索結果が 100 件程度で 512Mbyte 以上のメモリを消費するというのは異常です。1 件ヒットする度に巨大な連想配列やクラス・オブジェクトを発生させているということはありませんか? スクリプトの流れを見直してみてください。

参考サイト

(この項おわり)
header