MediaWiki APIでWikipediaの情報を取得

プログラミングブログパーツ

 拙作のブログパーツに
どこかの駅」「温泉地巡行」「神社★参詣
というのがあります。
いずれも、Wikipediaのページから情報を取得し、
画像などの情報を表示するという感じの作りになっています。

JavaScriptのクロスドメイン制約

 JavaScriptで組んでいるのですが、
クロスドメイン制約という問題があります。
それを回避するのに簡便なやり方として、PHPを使う手があります。
「JavaScript ➔ PHP ➔ 他サイトを参照」
のようにPHPを仲介することで回避できます。

 なので、
それで解決なのですが、
本サイトが利用しているXREAの無料版では、
1日あたりの転送量制限というのがあります。
ブログパーツはサイトのページに貼ってアクセサリ的に使われるという特性上、
自サイトで直接サービスした場合に、転送量制限に引っ掛かる恐れがないとも言えません。

 そこで、
JavaScriptだけでどうにかクロスドメイン問題を回避できないかと模索した結果、
見つけたのがYahooが無料でサービスしていたYQLでした。
クロスドメインで情報を参照できる仕組みを提供してくれていました。
自サイトで直接サービスする代わりに、
YQLを導入して作ったブログパーツのファイルを配布するという形でやっていました。
が、しかし2019-01-03をもってYQLは廃止されてしまいました(T_T)

 ということで、
それ以降はファイルの配布も中止し、
自サイトだけで動くように改修して、トップページの横に出していました。
そんな感じで、かれこれ2年弱になるのですね・・・。

MediaWiki API を使う

 先日、
ふと、そういえばWikipediaの情報を取得するAPIとかあったりしたのだろうか?
と調べてみたら・・・あらま、ありました(^_^;)
こちらのページが参考になります。
MediaWiki API というのを使えばやれるそうです。
しかもクロスドメイン問題を回避できる手段も提供されてました!
う~む、なぜAPIを探すという発想が今まで無かったのか・・・。
クロスドメイン問題の回避はPHPでやるのが簡単なので、
それでいいじゃんという思い込みが強かったようです(^_^;)

 さて、
MediaWiki APIこんな感じになっています。
パラメータがわんさかあって、どれを使えば良いのか困ってしまうくらいですね(^_^;)
幸いこちらにページ内容の取得方法が解説されています。
やり方が3つ提示されていますが、Parse API を使うのが良さそうです。
ズバリ、以下のようにやれば、Wikipediaの任意ページのHTMLを取得することができます。

https://ja.wikipedia.org/w/api.php?origin=*&format=json&action=parse&prop=text&page=ページ名

上記では日本語のWikipediaを参照するようになっています。
英語の場合は ja ではなく en になるはずです。
ページ名は例えば「富士山」とかになります。
UTF8なら問題ないはずですが、URIエンコードしておく方が無難だと思われます。
例えば、こちらのツールを使えば簡単にエンコードできます。
ちなみに「富士山」は「%E5%AF%8C%E5%A3%AB%E5%B1%B1」となりますね。

 なんと言っても肝心なのが、
これ ➔ 「origin=*
このパラメータによってクロスドメイン問題が回避されます。
レスポンスヘッダに「Access-Control-Allow-Origin: *」がセットされるので、
回避されるようです。(参考

 なお、
ブラウザでページにアクセスした結果のHTMLと、
APIで取得したHTMLは完全に同じものではないようです。
ブラウザでアクセスしたHTMLのうち、div.mw-parser-output 以下の内容が
APIで取得できるようになっている模様です。
余計なものが入っていない感じになるので、
むしろ都合が良いかもしれません。

実際のJavaScriptなコード例

 そして実際に、
Wikipediaのページから情報を取得する、
いわゆるスクレイピングを行うには、
例えば以下のようなコードを書いてやります。

var domParser = new DOMParser();

function requestWikiPage(name,onload,onerror) {
	var xhr = new XMLHttpRequest();
	xhr.onloadend = function() {
		var object;
		if (this.status === 200) {
			object = JSON.parse(this.response);
			if (object.error) {
				onerror('page not exist!');
			} else {
				onload(domParser.parseFromString(object.parse.text['*'],'text/html'));
			}
		} else {
			onerror(this.statusText);
		}
	};
	xhr.open('GET','https://ja.wikipedia.org/w/api.php?origin=*&format=json&action=parse&prop=text&page=' + name,true);
	xhr.send();
}

// 例えば↓こんな感じでスクレイピング。
requestWikiPage('%E5%AF%8C%E5%A3%AB%E5%B1%B1', function(dom) { // 富士山のページを読む。
	var a,trs;
		︙
	a = dom.querySelector('#coordinates a.external'); // 座標のリンク。
		︙
	trs = dom.querySelectorAll('table.infobox tr'); // テンプレート情報の各項目。
	trs = Array.prototype.slice.call(trs); // HTMLcollectionを配列に変換すると扱いやすくなる。
		︙
}, function(msg) {
	console.error(msg);
});

 少し解説してみます。
JavaScriptでやるので format=json 指定して、
JSONを返してもらうようにします。
XHRで読み込んだ結果を JSON.parse() してobjectに変換します。
xhr.responseType = 'json' とやればparseされたobjectが返るので、
その方がスマートかもしれません(^_^;))
ページが存在しなかった場合は、object.error に何か入っているので、
プロパティの存在チェックを行います。
エラーが無かった場合は、 object.parse.text['*'] に該ページのHTMLが入っているので、
DOMParser.parseFromString() を使ってDOMに変換します。
最後に、DocumentやElementに対して、
querySelector()querySelectorAll() を使って必要な情報を抽出します。

ブログパーツの配布を復活!

 ということで、
MediaWiki APIを導入することで、
クロスドメイン問題が回避できるようになりましたので、
ブログパーツの配布を復活します!(^_^)

 試してみるには、それぞれ以下を参照してください。
どこかの駅」はこちら
温泉地巡行」はこちら
神社★参詣」はこちら

Posted by 管理人