現在、Python Cookbook:
from xml.parsers import expat
class Element(object):
def __init__(self, name, attributes):
self.name = name
self.attributes = attributes
self.cdata = ''
self.children = []
def addChild(self, element):
self.children.append(element)
def getAttribute(self,key):
return self.attributes.get(key)
def getData(self):
return self.cdata
def getElements(self, name=''):
if name:
return [c for c in self.children if c.name == name]
else:
return list(self.children)
class Xml2Obj(object):
def __init__(self):
self.root = None
self.nodeStack = []
def StartElement(self, name, attributes):
element = Element(name.encode(), attributes)
if self.nodeStack:
parent = self.nodeStack[-1]
parent.addChild(element)
else:
self.root = element
self.nodeStack.append(element)
def EndElement(self, name):
self.nodeStack.pop()
def CharacterData(self,data):
if data.strip():
data = data.encode()
element = self.nodeStack[-1]
element.cdata += data
def Parse(self, filename):
Parser = expat.ParserCreate()
Parser.StartElementHandler = self.StartElement
Parser.EndElementHandler = self.EndElement
Parser.CharacterDataHandler = self.CharacterData
ParserStatus = Parser.Parse(open(filename).read(),1)
return self.root
私は約1 GBのサイズのXMLドキュメントで作業しています。誰もこれらを解析するより速い方法を知っていますか?
私は、プログラムからDOM機能を必要としないように見えます。次に、(c)ElementTreeライブラリーを使用します。 cElementTreeモジュールのiterparse関数を使用する場合、xmlを処理して、発生したイベントを処理できます。
ただし、cElementTree iterparse function の使用に関するFredriksのアドバイスに注意してください。
大きなファイルを解析するには、要素を処理したらすぐに要素を削除できます。
for event, elem in iterparse(source):
if elem.tag == "record":
... process record elements ...
elem.clear()
上記のパターンには1つの欠点があります。ルート要素はクリアされないため、多くの空の子要素を持つ単一の要素になります。ファイルが単に大きいというよりも巨大な場合、これが問題になる可能性があります。これを回避するには、ルート要素に手を加える必要があります。これを行う最も簡単な方法は、開始イベントを有効にし、変数の最初の要素への参照を保存することです。
# get an iterable
context = iterparse(source, events=("start", "end"))
# turn it into an iterator
context = iter(context)
# get the root element
event, root = context.next()
for event, elem in context:
if event == "end" and elem.tag == "record":
... process record elements ...
root.clear()
lxml.iterparse() はこれを許可しません。
前の例はPython 3.7では機能しません。最初の要素を取得するには、次の方法を検討してください。
# get an iterable
context = iterparse(source, events=("start", "end"))
is_first = True
for event, elem in context:
# get the root element
if is_first:
root = Elm
is_first = False
if event == "end" and elem.tag == "record":
... process record elements ...
root.clear()
CElementTreeモジュールを試しましたか?
cElementTreeは、xml.etree.cElementTreeとしてPython 2.5以降に含まれています。 ベンチマーク を参照してください。
削除された死んだImageShackリンク
lxml を使用することをお勧めします。これは、非常に高速なlibxml2ライブラリのpythonバインディングです。
私の経験では、libxml2とexpatのパフォーマンスは非常に似ています。しかし、私はlibxml2(およびpythonのlxml)の方がより積極的に開発およびテストされているように見えるため、好んでいます。また、libxml2にはより多くの機能があります。
lxmlは主に xml.etree.ElementTree と互換性のあるAPIです。また、Webサイトには優れたドキュメントがあります。
コールバックを登録すると、解析の速度が大幅に低下します。 [編集]これは、(高速)Cコードがpythonインタープリターを呼び出す必要があるためです。これはCほど高速ではありません。基本的に、Cコードを使用してファイルを読み取ります(速い)そして、DOMをPython(遅い)。[/ EDIT]でビルドします
Cで100%実装され、pythonコードへのコールバックなしでXMLを解析できるxml.etree.ElementTreeを使用してください。
ドキュメントが解析された後、それをフィルタリングして必要なものを取得できます。
それでも遅すぎてDOMが不要な場合は、ファイルを文字列に読み込んで、単純な文字列操作を使用して処理することもできます。
あなたのアプリケーションがパフォーマンスに敏感で、大きなファイル(あなたが言ったように、1GB以上)に出会う可能性が高いなら、私はstrongly単純な理由で質問であなたが示しているコードを使うことに対して助言するドキュメント全体をRAMにロードします。ドキュメントツリー全体を一度にRAMに保持することを避けるために。可能な限り)デザインを再考することをお勧めします。アプリケーションの要件がわからないため、適切に提案することはできません「イベントベース」の設計を使用しようとする一般的なアドバイス以外の特定のアプローチ。
expat ParseFileは、ツリー全体をメモリに保存する必要がない場合にうまく機能し、遅かれ早かれあなたのRAM大きなファイルの場合:
import xml.parsers.expat
parser = xml.parsers.expat.ParserCreate()
parser.ParseFile(open('path.xml', 'r'))
ファイルをチャンクに読み取り、RAMを爆発させることなくパーサーにフィードします。
Doc: https://docs.python.org/2/library/pyexpat.html#xml.parsers.expat.xmlparser.ParseFile
どうやら PyRXP は本当に速いです。
彼らは、それが最速のパーサーであると主張しています-しかし、cElementTreeは統計リストにありません。
私はこれを試すのにかなりの時間を費やしました。それは、lxmlとiterparseを使用することであり、不要なメモリを確実に解放することです。私の例では、arXivダンプの解析:
from lxml import etree
context = etree.iterparse('path/to/file', events=('end',), tag='Record')
for event, element in context:
record_id = element.findtext('.//{http://arxiv.org/OAI/arXiv/}id')
created = element.findtext('.//{http://arxiv.org/OAI/arXiv/}created')
print(record_id, created)
# Free memory.
element.clear()
while element.getprevious() is not None:
del element.getparent()[0]
そう element.clear
は十分ではありませんが、以前の要素へのリンクを削除することもできます。