web-dev-qa-db-ja.com

ElementTree反復構文

十分な大きさ(最大1GB)のxmlドキュメントを処理し、それらをpythonで解析する必要があります。 iterparse() 関数(SAXスタイルの解析)を使用しています。

私の懸念は以下です、あなたがこのようなxmlを持っていると想像してください

<?xml version="1.0" encoding="UTF-8" ?>
<families>
  <family>
    <name>Simpson</name>
    <members>
        <name>Homer</name>
        <name>Marge</name>
        <name>Bart</name>
    </members>
  </family>
  <family>
    <name>Griffin</name>
    <members>
        <name>Peter</name>
        <name>Brian</name>
        <name>Meg</name>
    </members>
  </family>
</families>

問題はもちろん、(シンプソンズのように)姓を取得しているときと、その家族の1人(たとえば、ホーマー)の名前を取得しているときを知ることです。

私がこれまで行ってきたことは、「スイッチ」を使用することです。これにより、「メンバー」タグ内にいるかどうかがわかります。コードは次のようになります。

import xml.etree.cElementTree as ET

__author__ = 'moriano'

file_path = "test.xml"
context = ET.iterparse(file_path, events=("start", "end"))

# turn it into an iterator
context = iter(context)
on_members_tag = False
for event, elem in context:
    tag = elem.tag
    value = elem.text
    if value :
        value = value.encode('utf-8').strip()

    if event == 'start' :
        if tag == "members" :
            on_members_tag = True

        Elif tag == 'name' :
            if on_members_tag :
                print "The member of the family is %s" % value
            else :
                print "The family is %s " % value

    if event == 'end' and tag =='members' :
        on_members_tag = False
    elem.clear()

そして、これは出力が正しいのでうまくいきます

The family is Simpson 
The member of the family is Homer
The member of the family is Marge
The member of the family is Bart
The family is Griffin 
The member of the family is Peter
The member of the family is Brian
The member of the family is Meg

私の懸念は、この(簡単な)例では、追加の変数を作成して、自分が処理しなければならない真のxmlの例(on_members_tag)がどのタグであるかを知る必要があり、さらにネストされたタグがあることです。

また、これは非常に縮小された例であるため、タグが多く、内部タグが多く、異なるタグ名や属性などを取得しようとしているxmlに直面していると想定できます。

だから質問です。ここでひどく愚かなことをしていますか?これにはもっとエレガントな解決策が必要だと思います。

可能なアプローチの1つは次のとおりです。パスリストを維持し、後方に覗いて親ノードを見つけます。

path = []
for event, elem in ET.iterparse(file_path, events=("start", "end")):
    if event == 'start':
        path.append(elem.tag)
    Elif event == 'end':
        # process the tag
        if elem.tag == 'name':
            if 'members' in path:
                print 'member'
            else:
                print 'nonmember'
        path.pop()
30
nneonneo

pulldom はこれに最適です。あなたはサックスストリームを取得します。ストリームを反復処理し、興味のあるノードを見つけたら、そのノードをdomフラグメントにロードします。

import xml.dom.pulldom as pulldom
import xpath # from http://code.google.com/p/py-dom-xpath/

events = pulldom.parse('families.xml')
for event, node in events:
    if event == 'START_ELEMENT' and node.tagName=='family':
        events.expandNode(node) # node now contains a dom fragment
        family_name = xpath.findvalue('name', node)
        members = xpath.findvalues('members/name', node)
        print('family name: {0}, members: {1}'.format(family_name, members))

出力:

family name: Simpson, members: [u'Hommer', u'Marge', u'Bart']
family name: Griffin, members: [u'Peter', u'Brian', u'Meg']
14