PythonでWikipediaのダンプからリンク先タイトルをcsv出力する
Wikipediaの全データから、各記事に含まれるリンク先を参照します。今回はBeauticulSoupを用います。
参考元: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 行と大きいため、表示までに数分要する場合があります。
例としてWikipedia- アンパサンドのページを見てみると、確かに「ラテン語」、「合字」、「記号」などにリンクが貼られていることがわかります。
目的のデータを出力したことが確認できました。
今回のデータは「あるページからのリンク先(to)」を示していますが、その逆方向である「あるページへ向かってリンクを貼っているページ(from)」を知りたいときには、今回得たリンクテキストを用いて解析が出来そうです。
また、Wikipediaのダンプダウンロードページ(リンク先)を見るとPythonを用いずにMySQLやPHPを用いた方法がありそうなため、両者の扱いに慣れたらそちらの方法も試したいと思います。