【Python】10GB以上のXMLデータ中のテキストデータを淡々と抜き出す。

# お相手は「ウィキペディア日本語版」のXMLファイル

久々に自然言語処理をしてみようと思い、[Wikipedia:データベースダウンロード](https://ja.wikipedia.org/wiki/Wikipedia:%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9%E3%83%80%E3%82%A6%E3%83%B3%E3%83%AD%E3%83%BC%E3%83%89)から「ウィキペディア日本語版」をダウンロードしてみた。
Wikipediaはクローリング禁止要請出してる代わりに、ダンプをダウンロードさせてくれるのである。素晴らしい...のだが、ダウンロードされるファイルは単一のXMLファイルである。当然ながらファイルサイズは、ダウンロード後解凍してみると、12GB超えと立派な大きさに。

```
$ ll
-rwxrwxrwx 1 k k 12927699165 Apr 12 17:17 xml_jawiki-20200401-pages-articles-multistream.xml*
```


headをしてみると...

```xml
<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.10/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.10/ http://www.mediawiki.org/xml/export-0.10.xsd" version="0.10" xml:lang="ja">
  <siteinfo>
    <sitename>Wikipedia</sitename>
    <dbname>jawiki</dbname>
    <base>https://ja.wikipedia.org/wiki/%E3%83%A1%E3%82%A4%E3%83%B3%E3%83%9A%E3%83%BC%E3%82%B8</base>
    <generator>MediaWiki 1.35.0-wmf.25</generator>
    <case>first-letter</case>
    <namespaces>
      <namespace key="-2" case="first-letter">メディア</namespace>
      <namespace key="-1" case="first-letter">特別</namespace>
      <namespace key="0" case="first-letter" />
      <namespace key="1" case="first-letter">ノート</namespace>
      <namespace key="2" case="first-letter">利用者</namespace>
      ...
      <namespace key="2302" case="case-sensitive">Gadget definition</namespace>
      <namespace key="2303" case="case-sensitive">Gadget definition talk</namespace>
    </namespaces>
  </siteinfo>
  <page>
    <title>Wikipedia:アップロードログ 2004年4月</title>
```

もう少し先に、進むとお目当てのテキストデータが見えてくる。
例:

```xml
    <title>情報工学</title>
    <ns>0</ns>
    <id>63</id>
    <revision>
      <id>76256715</id>
      <parentid>73769903</parentid>
      <timestamp>2020-02-18T20:19:50Z</timestamp>
      <contributor>
        <username>布田十番通り</username>
        <id>1352763</id>
      </contributor>
      <model>wikitext</model>
      <format>text/x-wiki</format>
      <text bytes="5880" xml:space="preserve">'''情報工学'''（じょうほうこうがく）は[[情報]]分野についての[[工学]]である。語感としては、[[情報科学]]という語がもっぱらおおまかに「科学」という語が指す範囲を中心としているのに対し、「工学」的な分野に重心があるが、内実としてはどれもたいして変わらないことが多い（たとえば、大学の学部学科名などに関しては、個々の大学の個性による違いのほうが、名前による違いより大きい）。日本で、大学の工学部などに[[計算機科学|コンピュータ科学]]ないし情報関係の学科を設置する際に、「工学」部という語との整合のためだけに便利に使われた、という面が大きい（[[情報工学科]]の記事を参照）。

なお英語の [[:en:Information engineering|information engineering]] は[[ソフトウェア工学]]における一手法であり、日本語の「情報工学」とは対応しない。また似た言葉に[[情報学]]がある。

== 概要 ==
ここでは、いくつかの大学の学科紹介などから（研究などにおける専門的な解説ではない）抜粋する。情報工学とは「[[情報]]」を工学的に利用するための学問分野である&lt;ref&gt;[http://www.ics.keio.ac.jp/dept/concept.html.ja 情報工学科のねらい] [[慶應義塾大学]] 情報工学科&lt;/ref&gt;。情報の発生（[[データマイニング]]、[[コンピュータグラフィックス]]など）、情報の伝達（[[コンピュータネットワーク]]など）、情報の収集（[[コンピュータビジョン]]、[[検索エンジン]]など）、情報の蓄積（[[データベース]]、[[データ圧縮]]など）、情報の処理（[[計算機工学]]、[[計算機科学]]、[[ソフトウェア工学]]）を扱う総合的な工学分野といえる&lt;ref&gt;[http://www.osakac.ac.jp/dept/p/zyuken/gakka2.html 情報工学とは何か] [[大阪電気通信大学]] 情報工学科&lt;/ref&gt;。また情報工学を、物理現象を支配している原理や法則や社会・経済活動を情報という観点から捉え，コンピュータ上の設計手順に変換することにより自動化する方法を創出する学問分野とする見方もあり、これは英語でいう[[コンピューティング]]（computing）に相当する&lt;ref&gt;[http://www.ise.shibaura-it.ac.jp/main02.html カリキュラム] [[芝浦工業大学]] 情報工学科&lt;/ref&gt;。いずれにしても以上の説明は、大学の学科紹介などからの抜粋である。

計算機科学や[[情報科学]]・情報工学を扱う学会としては、米国では発足が早かったこともあり、[[Association for Computing Machinery|ACM]]は直訳すると「計算機械学会」である。国際機関である[[情報処理国際連合]]の1960年発足の頃には、コンピュータは（数の）計算のみならず情報を処理する機械であるという認識は広まっており、日本の学会発足に関しても、和田弘により&lt;ref&gt;http://museum.ipsj.or.jp/pioneer/h-wada.html&lt;/ref&gt;「[[情報処理学会]]」の名が付けられ、[[情報処理]]という言葉が使われるようになった。また[[電子情報通信学会]]もこの分野をあらわす語として「情報」を使っている。

[[日本技術士会]]に「情報工学部会」があり、また同会が課している2次試験は部門別であるが、コンピュータソフトウェアに関連する部門を「情報工学部門」としている&lt;ref&gt;http://www.engineer.or.jp/c_categories/index02022.html&lt;/ref&gt;（[[技術士情報工学部門]]）。

* 学科名としては、[[京都大学]]（[[工学部]]）および[[大阪大学]]（[[基礎工学部]]）に、1970年初めて、情報工学科が登場する。同年、[[東京工業大学]]には情報科学科が、また、[[電気通信大学]]および[[山梨大学]]には計算機科学科が、[[金沢工業大学]]には情報処理工学科が設立された。
* 学部名としては、1986年に設置された九州工業大学の情報工学部が最初である。1987年から学生を受け入れ始めた知能情報工学科と電子情報工学科の2学科に加えて、制御システム工学科、機械システム工学科、生化システム工学科（現在のシステム創成情報工学科、機械情報工学科、生命情報工学科）の全5学科すべてが情報工学を専門とする学部である。また、[[情報科学]]を専門とする学部として、[[情報科学部]]が存在し、[[情報学]]を専門とする学部として、[[情報学部]]が存在する。[[工学]]系では1996年に[[大阪工業大学]]が最初に設置している。&lt;ref&gt;https://www.oit.ac.jp/is/&lt;/ref&gt;
* &lt;!--情報工学はComputer Scienceと訳すことが多い。--&gt;&lt;!-- ← は？　コンピュータサイエンスは計算機科学です--&gt;日本の大学の「[[情報工学科]]」では、英語名はComputer Science（計算機科学）としていることが多い。英語圏ではComputer Scienceの語が圧倒的に通りが良いし、たとえばIT業界で学歴として「CSの学位」といったように扱われるからである。information engineering とするところは、2007年時点で、8/33程度である。information engineering を掲げる例に、[[ケンブリッジ大学]]の Information Engineering Division がある&lt;ref&gt;[http://www.eng.cam.ac.uk/research/div-f/ CUED - Division F: Information Engineering]ケンブリッジ大学&lt;/ref&gt;。
* 研究科では[[情報学研究科]]など。
```

idや貢献者の情報なども含まれるようだが、取り急ぎ、欲しいのはタイトル(title)と本文(text)だろう。自然言語処理をする意味がありそうなのは、ある程度以上、本文の長さが長い場合だろうから、titleと本文の長さ一覧を作ってみるのが良さそうだ。
さて、pythonで巨大なXMLを扱うには...先人がいらっしゃった:
[数GB以上の巨大なXMLをPythonで読むときのメモリ節約法](https://qiita.com/shuntaroy/items/32e20e9bdab92aff0a89)

そうそう、直近の解析対象以外はメモリに読み込まない、メモリ節約が大事なんだよね。
メモリから溢れたデータを扱うと処理は極端に遅くなるので、一定の件数ごとに処理速度を記録して置くと良い。モノマネしながら、試行錯誤した結果がこちら。
# コード

```py
import xml.etree.ElementTree as ET #標準ライブラリのElementTreeを使用

body ="jawiki-20200401-pages-articles"
path =f"../xml_{body}-multistream.xml"
path_w =f"../short_{body}-title.csv"
context = ET.iterparse(path, events=('start', 'end'))

# rootなど不要そうなノードをスキップする
_, r0 = next(context)
_, r1 = next(context)
_, r2 = next(context)

print(r2.text)
count = 0
title =""
import time
start = time.time()
prev = start
with open(path_w, mode='w',encoding="utf-8") as fw:
    for event, elem in context:
        if event=="start" and elem.text:
            txt = elem.text
            if "title" in elem.tag:
                str = f"{elem.tag}".replace("{http://www.mediawiki.org/xml/export-0.10/}","")
                title = txt
            if "text" in elem.tag:
                str = f"{elem.tag}".replace("{http://www.mediawiki.org/xml/export-0.10/}","")
                if len(txt) <= 1000:
                    x= f"{title},{len(txt)}"
                    fw.write(x+"\n")
                    count += 1
                    if count % 1000 == 0: #1000件毎に処理にかかった時間を出力
                        next = time.time()
                        print(count, f"elapsed_time = {next - prev}", x)
                        prev = next

        elem.clear() #解析したelemをメモリから解放　・・★

print(count)

```
キモは★のところ。このclear()呼び出しをしないと手元のPCでは、40000件を読みだした頃から、処理速度がガクッと遅くなった。

#処理結果

```
636000 elapsed_time = 0.635291576385498 Wikipedia:削除依頼/Wikipedia:削除依頼/ログ/2019年11月18日,2692
637000 elapsed_time = 0.6260190010070801 Wikipedia:削除依頼/井上卓 (曖昧さ回避),1112
638000 elapsed_time = 0.6080121994018555 静的ウェブページ,7108
639000 elapsed_time = 0.6243636608123779 バンビーナ (芸能事務所),2590
640000 elapsed_time = 0.6858398914337158 涌井伶,1106
641000 elapsed_time = 0.632981538772583 槻木町 (池田市),6688
642000 elapsed_time = 0.5550098419189453 シモン・スルガ,2868
643000 elapsed_time = 1.671999454498291 My Sweet Maiden/Welcome To Our Diabolic Paradise,1955
644000 elapsed_time = 0.6001503467559814 郡山西部第二工業団地,1699
645000 elapsed_time = 0.6131837368011475 Template:UEFA U-17欧州選手権2009スペイン代表,1002
646000 elapsed_time = 0.6151120662689209 げんごや,1496
647000 elapsed_time = 0.6119420528411865 津島市立東小学校,2119
647933

Process finished with exit code 0
```
６０万件を過ぎても、1000件あたり1秒かそこらで処理できている。上等、上等。
出来あがったcsvは「タイトル,本文長」の並びで出来上がっている。

```csv
EU (曖昧さ回避),1480
生物,5850
地理,2217
こどもの文化,3866
日常生活,1764
情報工学,2650
文脈自由言語,2367
正規言語,1830
自然言語,1396
ゴーダチーズ,1142
宗教学,1767
タイ,1792
新聞学,2344
薬学,2338
ネマティック液晶,1209
Wikipedia:FAQ,1645
Wikipedia:議論が白熱しても冷静に,2269
音楽家,4380
メーリングリスト,3687
Wikipedia:FAQ 編集,4163
記録,2004
Wikipedia:画像提供依頼,2714
Wikipedia:データベースダウンロード,3331
日本の漫画家,5433
ゲームのタイトル一覧,2730
高橋しん,5675
寺沢武一,3951
```

比較の為、1000字以下のタイトルの方も抜き出してみた。

```
1321000 elapsed_time = 0.3331446647644043 Category:JA岐阜厚生連,165
1322000 elapsed_time = 0.19999384880065918 Category:台湾の女性,165
1323000 elapsed_time = 0.22401905059814453 桃源院殿,11
1324000 elapsed_time = 0.26406049728393555 カンパニアン階,14
1325000 elapsed_time = 0.2306044101715088 楜ヶ原川,934
1326000 elapsed_time = 0.25202035903930664 ストロンボシア科,27
1327000 elapsed_time = 0.1840074062347412 ユメクレ,23
1328000 elapsed_time = 0.21496915817260742 ウィキペディア・ライブラリー,28
1329000 elapsed_time = 0.22100353240966797 レート階,14
1330000 elapsed_time = 0.19901561737060547 Category:レバノンの総合競技大会選手,95
1331000 elapsed_time = 0.20599126815795898 春菴横町,12
1332000 elapsed_time = 0.24999594688415527 アンブロワーズ・マリー・フランソワーヌ・ジョセフ・パリソ・ド・ボーヴォワ,42
1333000 elapsed_time = 0.20502090454101562 Category:20世紀北アイルランドの女優,273
1333333

Process finished with exit code 0
```
...タイトルにCategoryを含むものは、別途使い道がありそう。

ということで、無事、数十分間で200万件のタイトルを含むXMLデータにアタリをつけることができた。
Thanks.