Wikipediaのダンプデータからタイトルを指定して記事を取り出す。

本記事の参考ぺージ

Wikipediaのダンプからページを取り出す #Python - Qiita

ダンプデータについて

Wikpediaのダンプ記事データにはindexとデータ本体の2種類がある。 indexを活用するとbz2ファイルを解凍せずに高速に記事にアクセスできる

  • indexデータ: jawiki-20241001-pages-articles-multistream-index.txt.bz2
  • 本体データ: jawiki-20241001-pages-articles-multistream.xml.bz2

indexデータの構成

indexデータは、以下のように構成される。一記事に1行1記事がある。
「index:page_id:記事名」という構成。

$ bzgrep "高城れに" "jawiki-20241001-pages-articles-mult
istream-index.txt.bz2"
1972156087:1771835:高城れに
3217498582:3500139:高城れにの週末ももクロ☆パンチ!!
3217498582:3500156:高城れにの週末ももクロ☆パンチ

index検索もとに必要なデータ。

次のindexに代わるindexが必要。一つのindexに約100記事ぐらいづつ含まれてるそうです。 下の例だと1972322566になる。その番号でindexが切り替わってる。

bzgrep -A 60 "高城れに$"  "jawiki-20241001-pages-artic
les-multistream-index.txt.bz2"
1972156087:1771835:高城れに
1972156087:1771836:株式会社TSUTAYA
1972156087:1771838:マーベル・ゾンビーズシリーズ
1972156087:1771840:マーベル・ゾンビーズ
1972156087:1771841:教育映画
1972156087:1771844:熱闘シリーズ
1972156087:1771846:マハヴィーラ
1972156087:1771847:Wikipedia:削除依頼/完全メイド宣言
1972156087:1771848:クリムゾン・キモノ
1972156087:1771850:第6実験室 予告版
1972156087:1771851:Wikipedia:削除依頼/コスプレ系飲食店
1972156087:1771853:百田夏菜子
1972156087:1771854:Category:日本の公立短期大学
1972156087:1771855:神戸県
1972156087:1771856:2004年欧州議会議員選挙 (イギリス)
1972156087:1771858:カール・マリア・ヴィリグート
1972156087:1771859:高知親局送信所
1972156087:1771860:東北7県
1972156087:1771864:東北圏
1972156087:1771903:ルーシャス・シェパード
1972156087:1771904:フェルナンド・カセレス
1972156087:1771912:ファミマガディスク
1972156087:1771920:カルレス・ブスケツ
1972156087:1771937:ザ・ローリング・ストーンズ シャイン・ア・ライト
1972156087:1771953:海洋底
1972156087:1771958:ECC外語学院
1972156087:1771977:Wikipedia:削除依頼/ファイル:ライダーズ ラプソディZ2改.jpg
1972156087:1771978:トーザ外語学院
1972156087:1771979:NDソフトスタジアム山形
1972156087:1771980:ヒューマン外語学院
1972156087:1771981:ファミマガDisk
1972156087:1771982:声 あなたと読売テレビ
1972156087:1771984:ベアトリス・デ・カスティーリャ・イ・グスマン
1972156087:1771985:Category:ノルウェーのノルディック複合選手
1972156087:1771986:豆盧通
1972156087:1771988:グアダルーペ (ヌエボ・レオン州)
1972156087:1771989:立命館大学の人物一覧
1972156087:1771993:Category:ノルウェーのクロスカントリースキー選手
1972156087:1771994:ベアトリス・デ・カスティーリャ・イ・モリナ
1972156087:1771995:インクレディブル・ハルク
1972156087:1771997:テクタイト
1972156087:1771998:Wikipedia:投稿ブロック依頼/利用者:210.199.5.151
1972156087:1771999:ロック・ボトム
1972322566:1772000:古市 (広島市)
1972322566:1772001:大谷敬二郎

本体から取り出す。

Pythonを利用して記事本体からindex情報、id情報、次のindex情報を使って取り出す。

高城れにの場合の情報

  • index: 1972156087
  • id: 1771835
  • 次のindex: 1972322566

コード

取り出した記事をtakagi.xlm にファイルとして保存する。out_file_nameで設定。

# ダンプファイルのパス
dump_file = 'jawiki-20241001-pages-articles-multistream.xml.bz2'
out_file_name="takagi"

offset = 1972156087 # インデックスで得たオフセット値
id= 1771835
next_offset = 1972322566

# ダンプファイルをバイナリモードで開き、指定のオフセットから読み込む
with open(dump_file, 'rb') as f:
    f.seek(offset)  # オフセットに移動
    block = f.read(next_offset-offset)   #一ブロックの読み込み
    data=bz2.decompress(block) #ブロックを解答
    xml = data.decode(encoding="utf-8") #ブロックを文字に
    
    #保存や出力など
    root = ET.fromstring("<root>" + xml + "</root>")
    page = root.find(f"page/[id='{id}']") 
    tree = ET.ElementTree(page)
    print(ET.tostring(page,encoding='unicode'))
    tree.write(f"{out_file_name}.xml", encoding="utf-8")

ワードから記事取得まで行うコード

import bz2
import xml.etree.ElementTree as ET

#index の検索
#search_term="高城れに"
out_file_name="Doraemon"
search_term="ドラえもん"
index_file="jawiki-20241001-pages-articles-multistream-index.txt.bz2"
indexs=[]
next_indexs=[]
count=0
old_tmp_offset=""
with bz2.open(index_file, 'rt', encoding='utf-8') as f:
    for line in f:
        if search_term in line:
            indexs.append(line)
            count=count+1

        tmp_offset=line.split(":")[0]
        if not (old_tmp_offset=="" or old_tmp_offset==tmp_offset):

            for no in range(count):
                next_indexs.append(tmp_offset)

            count=0
            #break

        old_tmp_offset=tmp_offset

print(next_indexs)
#quit()
#exit()
names=[i.split(":")[2].strip() for i in indexs]
offsets=[i.split(":")[0] for i in indexs]
ids=[i.split(":")[1] for i in indexs]
print(indexs)
closest_index = min(range(len(names)), key=lambda i: abs(len(names[i]) - len(search_term)))

word=(names[closest_index])
word_offset=(offsets[closest_index])

word_next_offset=(next_indexs[closest_index])
word_id=(ids[closest_index])

print(word)
print(word_offset)
print(word_next_offset)

# ダンプファイルのパス
dump_file = 'jawiki-20241001-pages-articles-multistream.xml.bz2'
#offset = 1972156087 # インデックスで得たオフセット値
#id= 1771835

offset = int(word_offset) # インデックスで得たオフセット値

next_offset = int(word_next_offset) # インデックスで得たオフセット値
id= int(word_id)

# ダンプファイルをバイナリモードで開き、指定のオフセットから読み込む
with open(dump_file, 'rb') as f:
    print("seek0")
    f.seek(offset)  # オフセットに移動
    print("seek1")

    block = f.read(next_offset-offset)  # 5000バイト分読み込む(必要に応じて調整)
    #block = f.read(1972322566-1972156087)  # 5000バイト分読み込む(必要に応じて調整)
    data=bz2.decompress(block)
    xml = data.decode(encoding="utf-8")
    root = ET.fromstring("<root>" + xml + "</root>")
    page = root.find(f"page/[id='{id}']")
    # 読み込んだデータから記事を抽出
    #start = data.find('<page>')
    #end = data.find('</page>') + len('</page>')

    #article = data[start:end]
    #print(article)
    #print(xml)
    tree = ET.ElementTree(page)
    print(ET.tostring(page,encoding='unicode'))
    tree.write(f"{out_file_name}.xml", encoding="utf-8")

まとめてワードリストを検索して早くする。

ワードリスト

ワードリストは「id ワード」の形式で用意する。 名前は、name_b_utf2.csv とした

$ head name_b_utf2.csv
1 スマホ
2 ハッシュタグ
3 Instagram
4 モデルプレス
5 ⭐
6 インスタグラム
7 Xperia

作業フォルダの作成

wikiのフォルダを作っておく

mkdir wiki

ワードリストの単語のindexを検索

indexは、wiki/index_list0.csvに保存される。

import bz2
import xml.etree.ElementTree as ET
import sys
import copy
import re

args=sys.argv
#index の検索

#出力ファイルノヘッダー
#id_name="wiki/"+args[1]
#search_term="高城れに"
#search_term="ドラえもん"

#search_term=args[2].strip().replace("\"","")

id0s=[]
search_terms=[]
with open("name_b_utf2.csv") as fin:
    for line in fin.readlines():
        line=line.strip().split(" ")
        id0s.append(line[0])
        search_terms.append(line[1])

#word_file=.strip().replace("\"","")
#index_file="jawiki-20241001-pages-articles-multistream-index.txt.bz2"

index_file="jawiki-20241001-pages-articles-multistream-index.txt"
indexs=[]
next_indexs=[]

file_id=[]
count=0
old_tmp_offset=""
#1print(id_name)
print(search_terms)
#quit()
#exit()
tmp_search_terms=copy.deepcopy(search_terms)

#with open(index_file, 'rt', encoding='utf-8') as f:
with bz2.open(index_file, 'rt', encoding='utf-8') as f:
    for line in f:
        #print(line)
        word0=re.sub(r'\(.*\)','',line.split(":")[2].strip())
        #print(word0)
        if any([word == word0 for word in tmp_search_terms]):
            tmp_search_terms.remove(word0)
            print(line)
            indexs.append(line)
            #count=count+1
        tmp_offset=line.split(":")[0]
        #if not (old_tmp_offset=="" or old_tmp_offset==tmp_offset):

        #    for no in range(count):
        #        next_indexs.append(tmp_offset)

        #    count=0
            #break

        #old_tmp_offset=tmp_offset



print(next_indexs)
names=[i.split(":")[2].strip() for i in indexs]
offsets=[i.split(":")[0] for i in indexs]
ids=[i.split(":")[1] for i in indexs]
print(indexs)

with open("wiki/index_list0.csv","w") as f_out:
    for name,offset,id in zip(names,offsets,ids):
        print((",").join([name,offset,id]))
        print((",").join([name,offset,id]),file=f_out)

exit()
quit()

保存フォーマットは、「単語,index,ID」。

$ head wiki/index_list0.csv
つの丸,4827164,931
バンダイナムコエンターテインメント,7926411,1413
森高夕次,14088537,2290
UEFAヨーロッパリーグ,17410483,2757
東海林さだお,24147253,3987
1315年,34488021,5572
原子軌道,36621488,5957
311年,48348477,8347
銅酸化物,50256325,8644
ダービーステークス,52737293,9320

その単語のindexの次のindexのデータに追加

$ cat wiki/wiki3b.py
import bz2
import xml.etree.ElementTree as ET
import sys
import copy
import re

args=sys.argv
#index の検索

#出力ファイルノヘッダー
#id_name="wiki/"+args[1]
#search_term="高城れに"
#search_term="ドラえもん"

#search_term=args[2].strip().replace("\"","")


#word_file=.strip().replace("\"","")
#index_file="jawiki-20241001-pages-articles-multistream-index.txt.bz2"

index_file="jawiki-20241001-pages-articles-multistream-index.txt"
indexs=[]
next_indexs=[]

file_id=[]
count=0
old_tmp_offset=""
next_list={}
with open(index_file, 'rt', encoding='utf-8') as f:
#with bz2.open(index_file, 'rt', encoding='utf-8') as f:
    for line in f:
        #print(line)
        #print(word0)
            #count=count+1
        tmp_offset=line.split(":")[0]
        if not (old_tmp_offset=="" or old_tmp_offset==tmp_offset):
           print(tmp_offset)
           next_list[old_tmp_offset]=tmp_offset

        old_tmp_offset=tmp_offset

next_list[old_tmp_offset]=tmp_offset


id_dic={}


with open("name_b_utf2.csv") as fin:
    for line in fin.readlines():
        line=line.strip().split(" ")
        id_dic[line[1]]=line[0]



print(next_list)


f_out=open("wiki/index_list1.csv","w")
with open("wiki/index_list0.csv") as f_in:
    for i in f_in.readlines():
        row=i.strip().split(",")
        #print(row)
        print(row[0]+","+row[1]+","+row[2]+","+next_list[row[1]]+","+id_dic[row[0]])
        print(row[0]+","+row[1]+","+row[2]+","+next_list[row[1]]+","+id_dic[row[0]],file=f_out)
f_out.close()

出力ファイル。
wiki/index_list1.csv。「単語,index,id,次のindex,ファイルについた単語の番号」

$ head wiki/index_list1.csv
つの丸,4827164,931,5291755,13626
バンダイナムコエンターテインメント,7926411,1413,8773576,159
森高夕次,14088537,2290,15289179,11033
UEFAヨーロッパリーグ,17410483,2757,18218333,2362
東海林さだお,24147253,3987,25181487,15986
1315年,34488021,5572,35237307,6828
原子軌道,36621488,5957,37327331,16392
311年,48348477,8347,49019995,19384
銅酸化物,50256325,8644,50980445,18317
ダービーステークス,52737293,9320,53241655,14347

記事データを得る。

以下のスクリプトで記事データを得る。出力は,wiki/1.xml, wiki2.xmlなど。 単語ファイルにつけたidでつく。

import bz2
import xml.etree.ElementTree as ET
import sys
import copy
import re

args=sys.argv
#index の検索

#出力ファイルノヘッダー
#id_name="wiki/"+args[1]
#search_term="高城れに"
#search_term="ドラえもん"

#search_term=args[2].strip().replace("\"","")


#word_file=.strip().replace("\"","")
#index_file="jawiki-20241001-pages-articles-multistream-index.txt.bz2"


with open("wiki/index_list1.csv") as f_in:
        for i in f_in.readlines():
#exit()
                i=i.strip().split(",")
        # ダンプファイルのパス
                dump_file = 'jawiki-20241001-pages-articles-multistream.xml.bz2'
        #offset = 1972156087 # インデックスで得たオフセット値
        #id= 1771835

                offset = int(i[1]) # インデックスで得たオフセット値
                id_name= int(i[4])
                next_offset = int(i[3]) # インデックスで得たオフセット値
                id= int(i[2])
        # ダンプファイルをバイナリモードで開き、指定のオフセットから読み込む
                with open(dump_file, 'rb') as f:
                        print("seek0")
                        f.seek(offset)  # オフセットに移動
                        print("seek1")

                        block = f.read(next_offset-offset)  # 5000バイト分読み込む(必要に応じて調整)
                        #block = f.read(1972322566-1972156087)  # 5000バイト分読み込む(必要に応じて調整)
                        data=bz2.decompress(block)
                        xml = data.decode(encoding="utf-8")
                        root = ET.fromstring("<root>" + xml + "</root>")
                        page = root.find(f"page/[id='{id}']")
                        # 読み込んだデータから記事を抽出
                        #start = data.find('<page>')
                        #end = data.find('</page>') + len('</page>')

                        #article = data[start:end]
                        #print(article)
                        #print(xml)
                        tree = ET.ElementTree(page)
                        print(ET.tostring(page,encoding='unicode'))
                        tree.write(f"{id_name}.xml", encoding="utf-8")