WikipediaのAPIを使ってランダムに記事本文を取得する

テキストの類似度を求める機械学習の練習をするために、Wikipediaの記事をランダムに集めようと思い立ちました。

これが簡単そうで意外に手こずりました。

今後同じようなことをする人のために手順をまとめておきます。

 

1.手動でWikipediaのランダム記事を取得する

これは簡単です。

https://ja.wikipedia.org/wiki/Special:Randompage

上記リンクをクリックするだけです。

Wikipediaのページ内にある「おまかせ表示」をクリックしても同じです。

 

2.WikipediaのAPIの概要

プログラムでランダムな記事を自動で取得する際にはAPIを使います。

そのAPIが微妙にわかりづらいので、ランダムにWikipediaの記事本文を取得するという用途に限って概要を先にお伝えします。

公式ドキュメントは以下のリンクです。

API:Main page – MediaWiki

情報が多すぎて圧迫されます。

日本語版Wikipediaのエンドポイントは「https://ja.wikipedia.org/w/api.php」です。

パラメータがずらずら書かれていますが、上記公式ドキュメントのAPI:Main pageの中で今回使うのは「action=query」と「format=json」だけです。

次はqueryについて詳しく見てみましょう。

API:Query – MediaWiki

ここにもパラメータがずらっと並べられていますが、使うのは基本的に「prop」と「list」だけです。

さらに「prop」や「list」として設定する値に応じて使えるパラメータが増えてきます。

パラメータが階層別にたくさんあるけれども実際に使うのは少しだけというのがポイントです。

 

3.APIを使ってWikipediaのランダム記事リストを取得する

それではAPIを使ってWikipediaのランダム記事リストを取得してみましょう。

API:Random – MediaWiki

この例をそのまま使います。

#!/usr/bin/python3

"""
    get_random.py

    MediaWiki API Demos
    Demo of `Random` module: Get request to list 5 random pages.

    MIT License
"""

import requests

S = requests.Session()

URL = "https://en.wikipedia.org/w/api.php"

PARAMS = {
    "action": "query",
    "format": "json",
    "list": "random",
    "rnlimit": "5"
}

R = S.get(url=URL, params=PARAMS)
DATA = R.json()

RANDOMS = DATA["query"]["random"]

for r in RANDOMS:
    print(r["title"])

例えば以下のように出力されます(毎回結果は異なります)。

Yaxham Light Railway
User talk:174.93.33.221
User talk:50.47.80.102
User talk:220.253.107.147
User talk:Antonioyuff

エンドポイントを日本語版にするのを忘れていました。

URL = “https://en.wikipedia.org/w/api.php”

URL = “https://ja.wikipedia.org/w/api.php”

に書き換えて実行します。

例えば次のように出力されます。

謝覧
利用者‐会話:Quanpuser
1対1 (データモデル)
利用者‐会話:アルカセット
Template‐ノート:Wide image

「利用者‐会話」や「Template‐ノート」といった通常の記事ではないものが表示されていますね。

通常の記事に限定しましょう。

パラメータに「rnnamespace=0」を加えます。

#!/usr/bin/python3

"""
    get_random.py

    MediaWiki API Demos
    Demo of `Random` module: Get request to list 5 random pages.

    MIT License
"""

import requests

S = requests.Session()

URL = "https://ja.wikipedia.org/w/api.php"

PARAMS = {
    "action": "query",
    "format": "json",
    "list": "random",
    "rnlimit": "5",
    "rnnamespace": "0",
}

R = S.get(url=URL, params=PARAMS)
DATA = R.json()

RANDOMS = DATA["query"]["random"]

for r in RANDOMS:
    print(r["title"])

結果の表示例です。

第5航空軍 (日本軍)
モラル・ハザード
レオミトレス
マッチウェンロックオリンピック
化石爬虫類の一覧

Wikipediaのランダム記事リストを取得するという目標は達成できました。

 

4.Wikipediaの記事本文のプレーンテキストを取得する

次に、Wikipediaの記事本文のプレーンテキストを取得することを考えます。

機械学習 – Wikipediaのページを例にとってやってみましょう。

結論を書きます。

import requests

#記事タイトルの設定
title = "機械学習"

#wikipediaに接続するための基本設定
S = requests.Session()
URL = "https://ja.wikipedia.org/w/api.php"

#記事本体を取得するためのパラメータの設定
ARTICLE_PARAMS = {
    "action": "query",
    "format": "json",
    "prop": "extracts",
    "explaintext": True,
    "exsectionformat": "plain",
    "titles": title
}

#記事タイトルから記事情報の取得
ARTICLE_R = S.get(url=URL, params=ARTICLE_PARAMS)
ARTICLE_DATA = ARTICLE_R.json()
pages = ARTICLE_DATA["query"]["pages"]
page_id = next(iter(pages))
print(pages[page_id]["extract"])

これで記事本文が出力されます。

ページIDを指定して取得するほうがスマートです。

「機械学習」の情報 – Wikipediaによると、機械学習の記事のページIDは185375です。

import requests

#記事ページIDの設定
page_id = "185375"

#wikipediaに接続するための基本設定
S = requests.Session()
URL = "https://ja.wikipedia.org/w/api.php"

#記事本体を取得するためのパラメータの設定
ARTICLE_PARAMS = {
    "action": "query",
    "format": "json",
    "prop": "extracts",
    "explaintext": True,
    "exsectionformat": "plain",
    "pageids": page_id
}

#ページIDから記事情報の取得
ARTICLE_R = S.get(url=URL, params=ARTICLE_PARAMS)
ARTICLE_DATA = ARTICLE_R.json()
print(ARTICLE_DATA["query"]["pages"][page_id]["extract"])

これでWikipediaの記事本文をプレーンテキストで取得する方法がわかりました。

 

5.WikipediaのAPIを使ってランダムに記事本文を取得する

先ほど記事本文を取得したときに使ったパラメータは「titles」や「pageids」のように複数形になっていることからもわかるように、複数の記事を一気に取得できます。

さらに、generatorを使えば、リクエストが1回で済むので、より望ましいです。

ということでgeneratorを使ってみましょう。

import requests

#wikipediaに接続するための基本設定
S = requests.Session()
URL = "https://ja.wikipedia.org/w/api.php"

#ランダム記事リストから記事本体を取得するためのパラメータの設定
GENERATOR_PARAMS = {
    #共通パラメータ
    "action": "query",
    "format": "json",
    #generatorパラメータ
    "generator": "random",
    "grnlimit": "5",
    "grnnamespace": "0",
    #記事取得本体パラメータ
    "prop": "extracts",
    "exintro": True,
    "explaintext": True,
    "exsectionformat": "plain",
}

#ランダム記事リストから記事情報の取得
GENERATOR_R = S.get(url=URL, params=GENERATOR_PARAMS)
GENERATOR_DATA = GENERATOR_R.json()
for k, v in GENERATOR_DATA["query"]["pages"].items():
    print(k, v)

API:Query – MediaWikiに書いてありますように、generatorのパラメータには名前の先頭にgをつけます。

記事取得本体パラメータのほうは先ほどと同じです。

ただ、「exintro」という先ほどはなかったパラメータが増えていることに注意してください。これは記事本文の最初の部分だけを取得するためのパラメータです。ここでは値をTrueとしていますが、値は何であっても(Falseでも0でも1でも何でも)パラメータが設定されているだけで有効になります。

「exintro」パラメータを設定しないと、”exlimit” was too large for a whole article extracts request, lowered to 1.エラーが発生して、一気にextract(記事本文)を取得することができませんでした。記事本文を一気に取得すると膨大なデータ量になるおそれがあるために運営側で制限しているのでしょう。

仕方がないのでランダムなページIDから一つずつ記事本体を取得します。

import requests
import time

#wikipediaに接続するための基本設定
S = requests.Session()
URL = "https://ja.wikipedia.org/w/api.php"

#wikipediaのランダム記事リスト(idとタイトル)を取得するためのパラメータの設定
RANDOM_PARAMS = {
    "action": "query",
    "format": "json",
    "list": "random",
    "rnlimit": "5",
    "rnnamespace": "0"
}

#記事本体を取得するためのパラメータの設定
ARTICLE_PARAMS = {
    "action": "query",
    "format": "json",
    "prop": "extracts",
    "explaintext": True,
    "exsectionformat": "plain",
}

#ランダム記事リストの取得
RANDOM_R = S.get(url=URL, params=RANDOM_PARAMS)
RANDOM_DATA = RANDOM_R.json()
RANDOMS = RANDOM_DATA["query"]["random"]

#ランダム記事リストから各記事本体を取得
for r in RANDOMS:
    #ページIDと記事タイトルを変数に格納
    page_id = r["id"]
    title = r["title"]

    #ページIDから記事情報の取得
    ARTICLE_PARAMS["pageids"] = str(page_id)
    ARTICLE_R = S.get(url=URL, params=ARTICLE_PARAMS)
    ARTICLE_DATA = ARTICLE_R.json()

    #結果の出力
    print(title)
    print(ARTICLE_DATA["query"]["pages"][str(page_id)]["extract"])
    print("-"*40)

    #高速でリクエストを繰り返すことで負担をかけないように1秒待つ
    time.sleep(1)

print("終了しました")

これでやりたいことができました。




コメントを残す

メールアドレスが公開されることはありません。