目次
サンプル・プログラムの実行例
サンプル・プログラム
wikisearch.php | サンプル・プログラム本体 |
サンプル・プログラムの流れ
次に、WebAPI の応答フォーマットがXMLかJSONかを識別する。後述するが、一般的なWebAPI にはXMLを返すものとJSONを返すものがあり、今回は学習用として、どちらにも対応できるような関数を用意している。
応答フォーマットがXMLのとき、WebAPI をコールして、サマリを返す関数が searchWikipediaSummaryXML である。Wikipedia API を呼び出したら、エラーチェックを行い、エラーが無ければ変数 $res にサマリーを、エラーがあれば FALSE を返す。
最後に、makeCommonBody 関数を使ってHTMLのbodyタグ部分を生成し、あらかじめ用意したヘッダとフッタをあわせて、ブラウザに表示する。
今後紹介するクラウド連携プログラムも、基本的にこのフローに沿って構築されている。
Wikipedia API
URL |
---|
https://ja.wikipedia.org/w/api.php |
フィールド名 | 要否 | 内 容 |
---|---|---|
format | 省略可 | xml, json, yaml等 |
action | 必須 | 操作:ここではquery |
prop | 省略可 | action固有のパラメータ。記事の各構成要素を取得する。ここではextractsを指定し、サマリを抽出する。 |
explaintext | 省略可 | 出力をHTMLではなくプレーンテキストにする。 |
redirects | 省略可 | リダイレクト記事を含める。 |
titles | 必須 | 見出し検索語。 |
解説:初期値など
14: // 初期化処理 ================================================================
15: //内部エンコード
16: define('INTERNAL_ENCODING', 'UTF-8');
17: mb_internal_encoding(INTERNAL_ENCODING);
18: mb_regex_encoding(INTERNAL_ENCODING);
19:
20: //自分自身のファイル名
21: define('MYSELF', basename($_SERVER['SCRIPT_NAME']));
22:
23: //参考サイトURL
24: define('REFERENCE', 'https://www.pahoo.org/e-soul/webtech/php06/php06-03-01.shtm');
25:
26: //プログラム・タイトル
27: define('TITLE', 'Wikipedia検索');
28:
29: //リファラチェック+リリースフラグの設定
30: if (isset($_SERVER['HTTP_HOST']) && ($_SERVER['HTTP_HOST'] == 'localhost')) {
31: define('FLAG_RELEASE', FALSE);
32: define('REFER_ON', '');
33: ini_set('display_errors', 1);
34: ini_set('error_reporting', E_ALL);
35: } else {
36: //リリース・フラグ(公開時にはTRUEにすること)
37: define('FLAG_RELEASE', TRUE);
38: //リファラ・チェック(直リン防止用;空文字ならチェックしない)
39: define('REFER_ON', 'www.pahoo.org');
40: }
41:
42: //応答フォーマット:切替可能
43: define('REQUEST_FORMAT', 'xml');
44: //define('REQUEST_FORMAT', 'json');
45:
46: //API呼び出しURL:変更不可
47: define('REQUEST_WIKIPEDIA', 'https://ja.wikipedia.org/w/api.php');
48:
49: //初期値
50: //表示幅(ピクセル)
51: define('WIDTH', 600);
52: //検索キーワード(デフォルト)
53: define('DEF_QUERY', 'PHP');
後述する応答フォーマットを REQUEST_FORMAT によって切り換えることができるようにしてある。
解説:サマリのリクエストURLを取得する
154: /**
155: * Wikipedia API:サマリのリクエストURLを取得する
156: * @param string $query 検索キーワード(UTF-8)
157: * @return string URL URL
158: */
159: function getURL_WikipediaAPI_summary($query) {
160: return REQUEST_WIKIPEDIA . '?format=' . REQUEST_FORMAT . '&action=query&prop=extracts&exintro&explaintext&redirects=1&titles=' . urlencode($query);
161: }
Wikipedia検索(サマリ):XML応答
163: /**
164: * Wikipedia検索(サマリ):XML応答
165: * @param string $query 検索キーワード(UTF-8)
166: * @param string $errmsg エラーメッセージを格納
167: * @return string サマリ/FALSE:エラー発生
168: */
169: function searchWikipediaSummaryXML($query, &$errmsg) {
170: $url = getURL_WikipediaAPI_summary($query); //リクエストURL
171:
172: //XMLエラーを有効にする
173: libxml_use_internal_errors(TRUE);
174:
175: //応答読み込み
176: $xml = @simplexml_load_file($url);
177:
178: //Wikipedia API接続失敗
179: if (count(libxml_get_errors()) > 0) {
180: $res = FALSE;
181: $errmsg = 'Wikipedia APIに接続できません';
182: //検索結果あり
183: } else if (isset($xml->query->pages->page->extract)) {
184: $res = $xml->query->pages->page->extract;
185: $errmsg = '';
186: //検索結果なし
187: } else {
188: $res = FALSE;
189: $errmsg = '検索結果がありません';
190: }
191:
192: return $res;
193: }
まず、検索キーワードをユーザー関数 getURL_WikipediaAPI_summary に渡し、リクエストURLを受け取る。
次に、 libxml_use_internal_errors 関数でXMLエラーを有効にしてから、 simplexml_load_file 関数で応答XMLを受け取る。
XMLエラーを有効にしたので、リクエストURLが反応しない場合は、 libxml_get_errors 関数にエラーが入る。結果の配列が無ければ、リクエストURLは何らかの応答を返している。
最後に、応答構造 $xml->query->pages->page->extract が存在するかどうかをチェックし、存在すれば、それがサマリーである。
無ければ、検索結果無しのエラーを返す。
解説:Wikipedia検索(サマリ):JSON応答
195: /**
196: * Wikipedia検索(サマリ):JSON応答
197: * @param string $query 検索キーワード
198: * @param string $errmsg エラーメッセージを格納
199: * @return string サマリ/FALSE:エラー発生
200: */
201: function searchWikipediaSummaryJSON($query, &$errmsg) {
202: $url = getURL_WikipediaAPI_summary($query); //リクエストURL
203:
204: $json = @file_get_contents($url);
205:
206: //Wikipedia API接続失敗
207: if ($json == FALSE) {
208: $res = FALSE;
209: $errmsg = 'Wikipedia APIに接続できません';
210: //JSON解釈
211: } else {
212: $res = FALSE;
213: $errmsg = '検索結果がありません';
214: $json = json_decode($json);
215: foreach ($json as $obj1) {
216: if (is_object($obj1)) {
217: foreach ($obj1 as $obj2) {
218: if (is_object($obj2)) {
219: foreach ($obj2 as $obj3) {
220: if (is_object($obj3)) {
221: if (isset($obj3->extract)) {
222: $res = (string)$obj3->extract;
223: $errmsg = '';
224: }
225: }
226: }
227: }
228: }
229: }
230: }
231: }
232: return $res;
233: }
一般的にWebAPIでは、応答がXMLの場合とJSONの場合とがあるので、ここでは学習のため、両方のユーザー関数を用意した。
まず、検索キーワードをユーザー関数 getURL_WikipediaAPI_summary に渡し、リクエストURLを受け取るところはユーザー関数 searchWikipediaSummaryXML と同じである。
JSONの場合、応答は file_get_contents 関数を使って受け取る。戻り値がFALSEであれば、リクエストURLが反応していないとしてエラーを返す。
戻り値があれば、 json_decode
が、応答構造が可変であるため、foreach を使ってオブジェクト・ツリーを下ってゆく。
HTML BODYを作成する
235: /**
236: * HTML BODYを作成する
237: * @param string $query 検索キーワード
238: * @param string $summary Wikipediaサマリ
239: * @param string $errmsg エラーメッセージ;エラー無しの時は空文字
240: * @param string $url リクエストURL
241: * @return string HTML BODY
242: */
243: function makeCommonBody($query, $summary, $errmsg, $url) {
244: $myself = MYSELF;
245: $refere = REFERENCE;
246: $width = WIDTH;
247: $p_title = TITLE;
248: $version = '<span style="font-size:small;">' . date('Y/m/d版', filemtime(__FILE__)) . '</span>';
249:
250: //表示内容を $msg へ
251: if ($errmsg == '') {
252: $msg = '<h3>検索結果</h3>' . nl2br($summary);
253: } else {
254: $msg = '<span style="color:red;">error: ' . $errmsg . '</span>';
255: }
256:
257: //デバッグ用表示
258: if (! FLAG_RELEASE) {
259: $phpver = phpversion();
260: $format = REQUEST_FORMAT;
261: $phpver =<<< EOT
262: PHPver : {$phpver}<br />
263: FORMAT : {$format}<br />
264: WebAPI : <a href="{$url}">{$url}</a><br />
265: <dl>
266:
267: EOT;
268: } else {
269: $phpver = '';
270: }
271:
272: $body =<<< EOT
273: <body>
274: <h2>{$p_title} {$version}</h2>
275: <form name="myform" method="GET" action="{$myself}" enctype="multipart/form-data">
276: 検索キーワード:
277: <input type="text" name="query" id="query" size="40" value="{$query}" />
278: <input type="submit" name="exec" value="検索" />
279: <input type="submit" name="reset" value="リセット" />
280: </form>
281: <div style="border-style:solid; border-width:1px; margin:20px 0px 0px 0px; padding:5px; width:{$width}px; overflow-wrap:break-word; word-break:break-all;">
282: {$msg}
283: </div>
284:
285: <div style="border-style:solid; border-width:1px; margin:20px 0px 0px 0px; padding:5px; width:{$width}px; font-size:small; overflow-wrap:break-word; word-break:break-all;">
286: <h3>使い方</h3>
287: <ol>
288: <li>[<span style="font-weight:bold;">検索キーワード</span>]に検索したいキーワードを入力してください.</li>
289: <li>[<span style="font-weight:bold;">検索</span>]ボタンを押してください.</li>
290: <li>検索結果を表示します.</li>
291: <li>[<span style="font-weight:bold;">リセット</span>]ボタンを押すと,初期化します.</li>
292: </ol>
293: ※参考サイト:<a href="{$refere}">{$refere}</a>
294: <p>{$phpver}</p>
295: </div>
296: </body>
297:
298: EOT;
299: return $body;
300: }
検索キーワード入力と結果表示を1つの関数で処理している。入力した検索キーワードはURL変数 query に追加し、再び自分自身(PHPプログラム)を呼び出す。頭で説明したサンプル・プログラムの流れの通り、query に値が入ることで検索処理を実行する仕組みである。
まず、エラーメッセージがあるかどうかをチェックし、無ければ「検索結果」の見出しを、あればエラーメッセージを赤色にして変数 $msg に代入する。変数 $msg は、このあと、結果表示用に利用する。
デバッグ用に定数 FLAG_RELEASE がFALSEのときは、PHPのバージョン、応答フォーマット(XMLまたはJSON)、WebAPIのリクエストURLを変数 $phpver に代入する。
最後に、変数 $body にbodyタグの内容を代入していく。
代入する内容が長いので、実際のHTMLタグを書くようにして代入ができるPHPのヒアドキュメント構文]を利用している。可変部分は "{$変数}:blue" のようにして、変数の値を代入することができる。
メイン・プログラム
302: // メイン・プログラム =======================================================
303: //初期値
304: $query = getParam('query', TRUE, DEF_QUERY);
305: $summary = $errmsg = '';
306:
307: //リセット
308: if (isButton('reset')) {
309: $query = DEF_QUERY;
310: }
311:
312: //検索実行
313: if ($query != '') {
314: if (REQUEST_FORMAT == 'xml') {
315: $summary = searchWikipediaSummaryXML($query, $errmsg);
316: } else {
317: $summary = searchWikipediaSummaryJSON($query, $errmsg);
318: }
319: }
320:
321: //リクエストURL
322: $url = getURL_WikipediaAPI_summary($query);
323:
324: $HtmlBody = makeCommonBody($query, $summary, $errmsg, $url);
325:
326: // 表示処理
327: echo $HtmlHeader;
328: echo $HtmlBody;
329: echo $HtmlFooter;
まず、URL変数として query があれば、それを検索キーワードとして変数 $query に代入する。
resetボタンが押下されたら、変数 $query の値をデフォルト値にする。
続いて、検索を実行する。
最後に、makeCommonBody を呼び出し、事前に用意してあるヘッダ $HtmlHeader、フッタ $HtmlFooter とあわせてブラウザに表示する。
メイン・プログラムで呼び出している、その他の関数については後述する。
指定したパラメータを取り出す
139: /**
140: * 指定したパラメータを取り出す
141: * @param string $key パラメータ名(省略不可)
142: * @param bool $auto TRUE=自動コード変換あり/FALSE=なし(省略時:TRUE)
143: * @param mixed $def 初期値(省略時:空文字)
144: * @return string パラメータ/NULL=パラメータ無し
145: */
146: function getParam($key, $auto=TRUE, $def='') {
147: if (isset($_GET[$key])) $param = $_GET[$key];
148: else if (isset($_POST[$key])) $param = $_POST[$key];
149: else $param = $def;
150: if ($auto) $param = mb_convert_encoding($param, INTERNAL_ENCODING, 'auto');
151: return $param;
152: }
引数 $key はパラメータ名である。
たとえば "wikisearch.php?query=PHP" のようにして呼び出されたら、$key に "query" を指定することで、値 "PHP" を返す。
日本語はURLエンコードされるので、受け取った値をデコードしたいときは、引数 $auto を TRUE にする。
URL変数が見当たらないときの初期値は、引数 $def に代入する。
引数 $auto および $def は、PHPの可変長引数リスト機能を使って省略可能にしてある。省略時には、イコールの右側の値が代入される。
URL変数は、GET渡しが優先で、無ければPOST渡しを探し、それでも無ければ $def の値を返す。
指定したボタンが押下されたか
128: /**
129: * 指定したボタンが押下されたか
130: * @param string $btn ボタン名
131: * @return bool TRUE=押された/FALSE=押されていない
132: */
133: function isButton($btn) {
134: if (isset($_GET[$btn]) && $_GET[$btn] != '') return TRUE;
135: if (isset($_POST[$btn]) && $_POST[$btn] != '') return TRUE;
136: return FALSE;
137: }
PHP5以上かどうか検査する
118: /**
119: * PHP5以上かどうか検査する
120: * @return bool TRUE:PHP5以上/FALSE:PHP5未満
121: */
122: function isphp5over() {
123: $version = explode('.', phpversion());
124:
125: return $version[0] >= 5 ? TRUE : FALSE;
126: }
プログラム冒頭で実行し、PHP5以上でなければエラーメッセージを表示して、強制終了する。
リファラ・チェック
102: //リファラ・チェック
103: if (REFER_ON != '') {
104: if (isset($_SERVER['HTTP_REFERER'])) {
105: $url = parse_url($_SERVER['HTTP_REFERER']);
106: $res = ($url['host'] == REFER_ON) ? TRUE : FALSE;
107: } else {
108: $res = FALSE;
109: }
110: } else {
111: $res = TRUE;
112: }
113: if (! $res) {
114: echo 'Please refer to ' . REFER_ON . ' !';
115: exit(1);
116: }
117:
118: /**
38: //リファラ・チェック(直リン防止用;空文字ならチェックしない)
39: define('REFER_ON', 'www.pahoo.org');
定数 REFER_ON には、本プログラムを配置したサーバのドメイン名をセットしておく。本プログラムが、このドメインから呼び出されていなければ、エラーメッセージを表示して、強制終了する。
また、定数 REFER_ON を空文字にすることで、このチェックを働かないようにすることができる。
エラー処理ハンドラ
86: /**
87: * エラー処理ハンドラ
88: */
89: function myErrorHandler($errno, $errmsg, $filename, $linenum) {
90: echo 'Sory, system error occured !';
91: exit(1);
92: }
93: error_reporting(E_ALL);
94: if (FLAG_RELEASE) $old_error_handler = set_error_handler('myErrorHandler');
自前のエラーハンドラ関数 myErrorHandler に差し替えて、簡単なエラーメッセージを表示して強制終了する。
質疑応答
プログラムをダウンロードして、自分のサイトで使用できるのか試してみましたが、システムエラーとしか表示されません。【回答】
Wikipediaの利用に際して、ユーザー登録などが必要なのでしょうか。APIのサイトを見ても自分ではよくわかりません。
自分のサイトで使用する場合、どこをどう変更するのか、そのあたりを教えてください。
自サイトで使用できないと送りましたが、
define('REFER_ON', '');
とするだけでよかったのですね。利用させていただきます。
二十四節気+節分・七十二候一覧を作成も利用させていただきます。
ご利用ありがとうございます。
説明不足でしたね。「リファラ・チェック」に補足説明をしました。
参考サイト
- MediaWiki API
- PHPでマッシュアップするための準備:ぱふぅ家のホームページ
今回は、PHPで WebAPI を利用する手始めとして、Wikipedia API を利用し、検索キーワードを入力し、ヒットした見出し語のサマリを表示するプログラムを作ってみることにする。
さらに、KAKASI を使った単語分解処理と組み合わせることで、用語の解説として Wikipedia のハイパーリンクを張ったようなサイトを構築することもできるだろう。
なお、本記事は作成当初 http://wikipedia.simpleapi.net を利用するプログラムを紹介していたが、2020年(令和2年)12月現在、このWebAPIは動作していないようなので、Wikipedia API を利用するプログラムに全面改訂した。