私はこのような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
動作しますが、かなり大きなファイルがあり、cProfile
はxpath
を使用すると非常に高価であることを示しています。
おそらく、無制限に多数のxml要素を反復処理するより効率的な方法があるのでしょうか。
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はcElementTree
のiterparse
よりも高速である可能性があります。 (表1を参照)。
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
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が付属しており、そのiterparse
はlxml.etree
iterparse
より高速です- lxmlドキュメント :
"" "大きなファイルの高いパーサースループットを必要とし、シリアル化をほとんどまたはまったく行わないアプリケーションの場合、cETが最適な選択です。また、少量のデータを抽出するか、大きなXMLデータセットから情報を集約しないiterparseアプリケーションでは、メモリに適合します。ただし、往復のパフォーマンスに関しては、lxmlは全体で数倍速くなる傾向があります。そのため、入力ドキュメントが出力よりも大幅に大きくない場合は、lxmlが最も優れています。 "" "