web-dev-qa-db-ja.com

xml要素を反復する効率的な方法

私はこのようなxmlを持っています:

<a>
    <b>hello</b>
    <b>world</b>
</a>
<x>
    <y></y>
</x>
<a>
    <b>first</b>
    <b>second</b>
    <b>third</b>
</a>

すべてを繰り返し処理する必要があります<a>および<b>タグですが、ドキュメントにどれだけあるかわかりません。だから私はxpathを使ってそれを処理します:

from lxml import etree

doc = etree.fromstring(xml)

atags = doc.xpath('//a')
for a in atags:
    btags = a.xpath('b')
    for b in btags:
            print b

動作しますが、かなり大きなファイルがあり、cProfilexpathを使用すると非常に高価であることを示しています。

おそらく、無制限に多数のxml要素を反復処理するより効率的な方法があるのでしょうか。

17
nukl

XPathは高速でなければなりません。 XPath呼び出しの数を1つに減らすことができます。

doc = etree.fromstring(xml)
btags = doc.xpath('//a/b')
for b in btags:
    print b.text

それが十分に高速でない場合は、 Liza Dalyのfast_iter を試してください。これには、XML全体を最初にetree.fromstringで処理する必要がないという利点があり、親ノードは、子がアクセスされた後に破棄されます。これらは両方とも、メモリ要件の削減に役立ちます。以下は fast_iterの変更バージョン であり、不要になった他の要素の削除についてより積極的です。

def fast_iter(context, func, *args, **kwargs):
    """
    fast_iter is useful if you need to free memory while iterating through a
    very large XML file.

    http://lxml.de/parsing.html#modifying-the-tree
    Based on Liza Daly's fast_iter
    http://www.ibm.com/developerworks/xml/library/x-hiperfparse/
    See also http://effbot.org/zone/element-iterparse.htm
    """
    for event, elem in context:
        func(elem, *args, **kwargs)
        # It's safe to call clear() here because no descendants will be
        # accessed
        elem.clear()
        # Also eliminate now-empty references from the root node to elem
        for ancestor in elem.xpath('ancestor-or-self::*'):
            while ancestor.getprevious() is not None:
                del ancestor.getparent()[0]
    del context

def process_element(elt):
    print(elt.text)

context=etree.iterparse(io.BytesIO(xml), events=('end',), tag='b')
fast_iter(context, process_element)

Liza Dalyの記事 大きなXMLファイルの解析については、読者にとっても役立つでしょう。記事によると、fast_iterを使用したlxmlはcElementTreeiterparseよりも高速である可能性があります。 (表1を参照)。

21
unutbu

iter はどうですか?

>>> for tags in root.iter('b'):         # root is the ElementTree object
...     print tags.tag, tags.text
... 
b hello
b world
b first
b second
b third
10
user225312

Iterparseを使用します。

   import lxml.etree as ET
   for event, elem in ET.iterparse(filelike_object):
        if elem.tag == "a":
            process_a(elem)
            for child in elem:
                process_child(child)
            elem.clear() # destroy all child elements
        Elif elem.tag != "b":
            elem.clear()

これによってすべてのメモリが節約されるわけではありませんが、この手法を使用して、Gbを超えるXMLストリームを処理することができました。

試してみてくださいimport xml.etree.cElementTree as ET ... Pythonが付属しており、そのiterparselxml.etreeiterparseより高速です- lxmlドキュメント

"" "大きなファイルの高いパーサースループットを必要とし、シリアル化をほとんどまたはまったく行わないアプリケーションの場合、cETが最適な選択です。また、少量のデータを抽出するか、大きなXMLデータセットから情報を集約しないiterparseアプリケーションでは、メモリに適合します。ただし、往復のパフォーマンスに関しては、lxmlは全体で数倍速くなる傾向があります。そのため、入力ドキュメントが出力よりも大幅に大きくない場合は、lxmlが最も優れています。 "" "

5
John Machin