home about terms twitter

PythonでWikipediaのダンプからリンク先タイトルをcsv出力する

date: 2020-04-08 | mod: 2020-04-08

Wikipediaの全データから、各記事に含まれるリンク先を参照します。今回はBeauticulSoupを用います。

  • Wikipedia …フリー百科事典(公式サイト
  • BeautifulSoup …HTMLやXMLからデータを抽出するPythonライブラリ(公式サイト

参考元:Ahogrammer - Wikipediaのリンクを解析して同義語を抽出する

参考元はjsonによる出力を行っているため、jsonファイルが必要な場合はそちらをご参照ください。
今回は個人的に扱いなれており、pandasでも扱いやすいcsv形式での出力を行います。

index


コード全体

動作環境:

  • python 3.6.8
  • Anaconda
  • beautifulsoup4
input output
WikiExtractorのextractedディレクトリ(後述) result.csv
import json, glob, csv
from urllib.parse import unquote
from collections import defaultdict, Counter
from bs4 import BeautifulSoup

files = glob.glob('extracted/**/wiki*')

with open('result.csv', 'w', encoding="utf-8", newline='') as f:  # 保存先のcsvファイル
    header = ['title', 'link_list']  # csvの設定1
    writer = csv.writer(f)  # csvの設定2
    writer.writerow(header)  # csvの設定3
    for file in files:
        print(file)
        with open(file, encoding="utf-8") as f:
            texts = f.read()
        soup = BeautifulSoup(texts)
        for page in soup.find_all('doc'):
            title = page['title']
            link_list = []  # リンクテキストをリストとして回収する
            for link in page.find_all('a', href=True):
                if link.has_attr('href'):
                    href = unquote(link['href'])
                    text = link.text
                    if text == '':  # 空のリンクテキストを削除する
                        pass
                    else:
                        link_list.append(text)
            link_list_sort = ', '.join(list(set(link_list)))  # リンクテキスト1, リンクテキスト2 ...
            writer.writerow([title, link_list_sort])  # csvファイルにページタイトルとリンクテキストの集合を書き込む

合計で10 GB近いサイズのファイルを扱うため、ストレージの容量にご注意ください。
また、環境によりますが、最終的な出力を得るまでに合計で10時間ほどの時間が見込まれます。

重要な箇所を以下に示します。


ダンプ取得と本文抽出

Wikipediaのダンプを公式のページからダウンロードします。ダウンロード手順は以下のリンク先に従ってください(リンク先)。 「pages-articles.xml.bz2 : ノートページ、利用者ページを除く最新版のダンプ」を取得して使用します。今回は「jawiki-20200401-pages-articles.xml.bz2」を取得しました。サイズの大きいデータ(解凍前で3.00 GB)を扱います。

得られたダンプから本文を抽出します。抽出には「Wikiextractor」を用います。git cloneまたは直接ダウンロードすることでWikiextractorプロジェクトのディレクトリを任意の場所にコピーします。

その後、作成したWikiextractorを用いて本文を抽出します。

python WikiExtractor.py -o extracted --lists --links jawiki-20200401-pages-articles.xml.bz2

環境に依存すると思われますが、全て抽出するまでに5時間を要しました。テキストのファイルは解凍前が3.00 GB、解凍後が7.65 GBとなりました。

リンクの取得

目的とするページのタイトルを検索し、そこに含まれるリンクを取得します。
上述のコードを実行することでresult.csvが出力されます。

要点のみ以下に示します。

if text == '':  # 空のリンクテキストを削除する
    pass
else:
    link_list.append(text)

空のリンクテキストがリストに含まれてしまっていたので、それを削除します。
forループの中でややネストしてしまっており、実行時間に影響があると思われますが、これにより空のリンクテキストを削除することができます。

link_list_sort = ', '.join(list(set(link_list)))  # リンクテキスト1, リンクテキスト2 ...
writer.writerow([title, link_list_sort])  # csvファイルにページタイトルとリンクテキストの集合を書き込む

一ページの中でリンクテキストが重複していることがあるため、set()で重複を削除しています。
その後、リンクテキストは出来るだけシンプルな形で保存したかったため、join()を用いてカンマ区切りで得られるようにしています。

コードの実行から終了までにおよそ5時間を要しました。得られたcsvファイルのサイズは800 Mbとなりました。

出力

出力されたcsvファイルをDB Browser for SQLiteにインポートして確認してみます。
冒頭に載せた画像と同じ画像ですが、再度以下に示します。
行数が1,194,794 行と大きいため、表示までに数分要する場合があります。

python-wikipedia-dump-link-0.jpg

例としてWikipedia- アンパサンドのページを見てみると、確かに「ラテン語」、「合字」、「記号」などにリンクが貼られていることがわかります。

目的のデータを出力したことが確認できました。

今回のデータは「あるページからのリンク先(to)」を示していますが、その逆方向である「あるページへ向かってリンクを貼っているページ(from)」を知りたいときには、今回得たリンクテキストを用いて解析が出来そうです。

また、Wikipediaのダンプダウンロードページ(リンク先)を見るとPythonを用いずにMySQLやPHPを用いた方法がありそうなため、両者の扱いに慣れたらそちらの方法も試したいと思います。