2017-11-09 3 views
1

etree.iterparseを使用してGB ++サイズのXML Wikipediaダンプファイルを解析するPython 3.4。私は現在<page>の要素の中で<ns>の値に応じてテストしたいと思います。後者の値に応じて、<page>オブジェクト全体のソースXMLとその中に入れ子になっている要素、つまり記事全体のXML 。Python tree.iterparseエクスポートソースすべての子孫を含む選択された要素のXML

私はオブジェクトを反復して欲しいものを見つけることができますが、使用可能なすべての関数はテキスト/属性値を読みたいと思われますが、ソースファイルのXMLコードの完全なスコープ<page>オブジェクト。これは可能ですか?

XMLのカットダウン版は次のようになります。

<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.10/" xml:lang="en"> 
    <page> 
    <title>Some Article</title> 
    <ns>0</ns> 
    <revision> 
     <timestamp>2017-07-27T00:59:41Z</timestamp> 
     <text xml:space="preserve">some text</text> 
    </revision> 
    </page> 
    <page> 
    <title>User:Wonychifans</title> 
    <ns>2</ns> 
    <revision> 
     <text xml:space="preserve">blah blah</text> 
    </revision> 
    </page> 
</mediawiki> 

<ns>値テストに私を取得するPythonのコードはここにある:

私はしたいと思います。この場合
``from lxml import etree 

# store namespace string for all elements (only one used in Wikipedia XML docs) 
NAMESPACE = '{http://www.mediawiki.org/xml/export-0.10/}' 
ns = {'wiki' : 'http://www.mediawiki.org/xml/export-0.10/'} 

context = etree.iterparse('src.xml', events=('end',)) 
for event, elem in context: 
    # at end of parsing each 
    if elem.tag == (NAMESPACE+'page') and event == 'end': 
    tagNs = elem.find('wiki:ns',ns) 
    if tagNs is not None: 
     nsValue = tagNs.text 
     if nsValue == '2': 
     # export the current <page>'s XML code 

XXXコードを抽出するには、の2番目の<page>要素、つまり、次の文字列を保持する文字列を使用します。

<page> 
    <title>User:Wonychifans</title> 
    <ns>2</ns> 
    <revision> 
     <text xml:space="preserve">blah blah</text> 
    </revision> 
    </page> 

編集:マイナータイプミスとより良いマークアップ

答えて

1

あなたはこれを行うことができます。

>>> from lxml import etree 
>>> mediawiki = etree.iterparse('mediawiki.xml') 
>>> page_content = {} 
>>> for ev, el in mediawiki: 
...  if el.tag=='page': 
...   if page_content['ns']=='2': 
...    print (page_content) 
...   page_content = {} 
...  else: 
...   page_content[el.tag.replace('{http://www.mediawiki.org/xml/export-0.10/}', '')] = \ 
...    el.text.strip() if el.text else None 
... 
>>> page_content 
{'mediawiki': '', 'revision': '', 'timestamp': '2017-07-27T00:59:41Z', 'title': 'User:Wonychifans', 'page': '', 'text': 'blah blah', 'ns': '2'} 

出力xmlの構造は非常に単純なので、辞書から構築するのは難しくありません。

編集:このアプローチでは、xmlファイルを2回通過する必要がありますが、これは高速になり、必要なxmlを回復します。

まず、page要素の開始行を探します。

>>> from lxml import etree 
>>> mediawiki = etree.iterparse('mediawiki.xml', events=("start", "end")) 
>>> for ev, el in mediawiki: 
...  tag = el.tag[1+el.tag.rfind('}'):] 
...  if ev=='start' and tag=='page': 
...   keep=False 
...  if ev=='start' and tag=='ns' and el.text=='2': 
...   keep=True 
...  if ev=='end' and tag=='page' and keep: 
...   print (el.sourceline) 
... 
10 

開始点を使用して完全なpageエントリを見つけるためにもう一度xmlを実行します。

>>> with open('mediawiki.xml') as mediawiki: 
...  for _ in range(9): 
...   r = next(mediawiki) 
...  for line in mediawiki: 
...   print (line.strip()) 
...   if '</page>' in line: 
...    break 
...   
<page> 
<title>User:Wonychifans</title> 
<ns>2</ns> 
<revision> 
<text xml:space="preserve">blah blah</text> 
</revision> 
</page> 
+0

ありがとうございますが、XMLの例を簡略化し、XMLを再ビルドすると、プロセス全体が想像よりも複雑になります(さらに、それを行う方法もわかりません) 。 – mwra

+0

編集のもう1つのアプローチ。 –

+0

ありがとうございます。確かに2番目のコードはXMLファイル全体をロードする必要があり、この場合は65GBです。だから、私はiterparseを使っているのです(より正確には、このタイプのc.10GBファイルの8GB RAM MacBookAir上の他のタスクにもうまく使っています)。もう一つの問題は、コードでは ' 'がすべて同じ長さであると仮定しているため、最初のパスから' el.sourceline'値のリストを繰り返す必要があると思います。 – mwra

1

最終的な解決策に私が役立ったので、私はBill Bellの答えを私に示しました。その中核は以下のとおりです。外側のループは50以上のソースXMLファイルをループさせます。

一部のソースが大きいため、コピーされたソースデータが1GBを超えているかどうかをチェックします。その場合、ファイルへのデータの書き込みが発生し、バッファ文字列変数がパージされます。それ以外の場合、抽出されたすべてのデータはソースファイルの読み込みの終わりに書き込まれます。

出力ファイルのサイズを監視し、指定されたサイズを超えたら出力ソースを切り替えます。この場合、スクリプトの実行ごとにソースセット全体をスキャンする方が簡単でした。

私は簡潔にするため、いくつかのログ& print文を削除した

<!-- language: lang-python --> 

import sys 

dataSourceStr = '/Users/x/WP-data/' 
outputDataStr = '/Users/x/WP-data/ns-data/' 
headfile = open("header.txt","r") 
headStr = headfile.read() 
headfile.close() 
footStr = '</mediawiki>' 
matchCount = 0 
strPage = '' 
strPage = headStr 
fileNum = 20 
nameSpaceValue = 4 
startNum = 41 # starting file number 
lastNum = 53 # ending file number 
endNum = lastNum + 1 
outputDataFile = outputDataStr + 'ns' + str(nameSpaceValue) + '.xml' 

for fileNum in range (startNum , endNum): 
    with open(dataSourceStr + str(fileNum) + '.xml') as mediawiki: 
    lineNum = 44 
    blnKeep = False 
    strPage = '' 
    strItem = '' 
    loopMatchCount = 0 
    for _ in range(lineNum): 
     r = next(mediawiki) 
    for line in mediawiki: 
     if '<ns>' + str(nameSpaceValue) + '</ns>' in line: 
     blnKeep = True 
     matchCount = matchCount + 1 
     loopMatchCount = loopMatchCount + 1 
     strItem = strItem + line 
     lineNum = lineNum + 1 
     if '</page>' in line: 
     if blnKeep: 
      strPage = strPage + strItem 
      strItem = '' 
      blnKeep = False 
      strPageSize = sys.getsizeof(strPage) 
      if strPageSize > 1073741824: 
      file = open(outputDataFile,"a") 
      file.write(strPage) 
      file.close() 
      strPage = '' 
     else: 
      strItem = '' 

    mediawiki.close 
    file = open(outputDataFile,"a") 
    file.write(strPage) 
    file.close() 

file = open(outputDataFile,"a") 
file.write(footStr) 
file.close() 

私は、これはよりエレガントな可能性が確信しているが、私は、これはすべての仲間の非専門家がここに到着し、この種をやろうとして役に立てば幸い物の

関連する問題