PHPで「Wikipedia API」を利用する

(1/1)
フリーの百科事典として有名な「Wikipedia」にも Wikipedia API と呼ぶ WebAPI が用意されている。検索キーワードにヒットする見出し語のサマリなどを返してくれるWebサービスで、利用料はかからない。
今回は、PHPで WebAPI を利用する手始めとして、Wikipedia API を利用し、検索キーワードを入力し、ヒットした見出し語のサマリを表示するプログラムを作ってみることにする。
さらに、KAKASI を使った単語分解処理と組み合わせることで、用語の解説として Wikipedia のハイパーリンクを張ったようなサイトを構築することもできるだろう。

なお、本記事は作成当初 http://wikipedia.simpleapi.net を利用するプログラムを紹介していたが、2020年(令和2年)12月現在、このWebAPIは動作していないようなので、Wikipedia API を利用するプログラムに全面改訂した。

目次

サンプル・プログラムの実行例

PHPで「Wikipedia API」を利用する

サンプル・プログラム

圧縮ファイルの内容
wikisearch.phpサンプル・プログラム本体

サンプル・プログラムの流れ

PHPで「Wikipedia API」を利用する
まず、変数 $query に検索キーワードが入っていたら、検索処理のルートに入る。
次に、WebAPI の応答フォーマットがXMLかJSONかを識別する。後述するが、一般的なWebAPI にはXMLを返すものとJSONを返すものがあり、今回は学習用として、どちらにも対応できるような関数を用意している。

応答フォーマットがXMLのとき、WebAPI をコールして、サマリを返す関数が searchWikipediaSummaryXML である。Wikipedia API を呼び出したら、エラーチェックを行い、エラーが無ければ変数 $res にサマリーを、エラーがあれば FALSE を返す。

最後に、makeCommonBody 関数を使ってHTMLのbodyタグ部分を生成し、あらかじめ用意したヘッダとフッタをあわせて、ブラウザに表示する。

今後紹介するクラウド連携プログラムも、基本的にこのフローに沿って構築されている。

Wikipedia API

Wikipedia API は、入力パラメータ(IN)はGET渡しで、出力結果(OUT)はXMLやJSONで返ってくるという無償のWebAPIである。
WebAPIのURL
URL
https://ja.wikipedia.org/w/api.php

入力パラメータ
フィールド名 要否 内  容
format 省略可 xml, json, yaml等
action 必須 操作:ここではquery
prop 省略可 action固有のパラメータ。記事の各構成要素を取得する。ここではextractsを指定し、サマリを抽出する。
explaintext 省略可 出力をHTMLではなくプレーンテキストにする。
redirects 省略可 リダイレクト記事を含める。
titles 必須 見出し検索語。
応答データ(xml) api query pages page extract サマリー

解説:初期値など

  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');

 define  関数を使って、いくつかの初期値を定義している。変更不可としている定数以外は自由に改変できる。
後述する応答フォーマットを 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: }

ユーザー関数 getURL_WikipediaAPI_summary は、検索キーワードを引数にして、Wikipedia API のリクエストURLを返す。検索キーワードはUTF-8で渡すこと。

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: }

ユーザー関数 searchWikipediaSummaryXML は、検索キーワードとエラーメッセージを格納する変数を引数にして、XMLで応答結果(サマリー)を受け取る場合に使う。

まず、検索キーワードをユーザー関数 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: }

ユーザー関数 searchWikipediaSummaryJSON は、検索キーワードとエラーメッセージを格納する変数を引数にして、JSONで応答結果(サマリー)を受け取る場合に使う。
一般的に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: }

検索キーワード、サマリ、エラーメッセージ、リクエストURLを引数として、ブラウザに表示するHTMLのbodyタグを生成するのがユーザー関数 makeCommonBody である。
検索キーワード入力と結果表示を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: }

ユーザー関数 getParam は、本プログラムを実行したURLからパラメータを取り出す。

引数 $key はパラメータ名である。
たとえば "wikisearch.php?query=PHP" のようにして呼び出されたら、$key に "query" を指定することで、値 "PHP" を返す。
日本語はURLエンコードされるので、受け取った値をデコードしたいときは、引数 $autoTRUE にする。
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: }

ユーザー関数 isButton は、本プログラムを呼び出した画面で指定したボタンが押下されたかどうかを調べる。

考え方は getParam と同じである。値を返さず、そのパラメータがURL変数に存在するかどうかをチェックして返す。

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: }

ユーザー関数 isphp5over は、動作環境がPHP5以上かどうかを調べる。
プログラム冒頭で実行し、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');

定数 FLAG_RELEASETRUE のときには、PHPのエラーメッセージ表示を抑制するための処理である。
自前のエラーハンドラ関数 myErrorHandler に差し替えて、簡単なエラーメッセージを表示して強制終了する。

質疑応答

【質問】かんちゃんさん
プログラムをダウンロードして、自分のサイトで使用できるのか試してみましたが、システムエラーとしか表示されません。
Wikipediaの利用に際して、ユーザー登録などが必要なのでしょうか。APIのサイトを見ても自分ではよくわかりません。
自分のサイトで使用する場合、どこをどう変更するのか、そのあたりを教えてください。
 
自サイトで使用できないと送りましたが、
  define('REFER_ON', '');
とするだけでよかったのですね。利用させていただきます。
二十四節気+節分・七十二候一覧を作成も利用させていただきます。
【回答】
ご利用ありがとうございます。
説明不足でしたね。「リファラ・チェック」に補足説明をしました。

参考サイト

(この項おわり)
header