Вот хороший ответ о xml.etree.ElementTree.iterparse
практике с огромными файлами XML.lxml
также имеет метод.Ключом к анализу потока с помощью iterparse
является ручная очистка и удаление уже обработанных узлов, поскольку в противном случае у вас закончится нехватка памяти.
Другой вариант - использование xml.sax
.Официальное руководство слишком формально для меня, и в нем нет примеров, поэтому оно нуждается в разъяснении вместе с вопросом.Модуль синтаксического анализа по умолчанию, xml.sax.expatreader
, реализует интерфейс инкрементального анализа xml.sax.xmlreader.IncrementalParser
.То есть xml.sax.make_parser()
предоставляет подходящий анализатор потока.
Например, для потока XML, например:
<?xml version="1.0" encoding="utf-8"?>
<root>
<entry><a>value 0</a><b foo='bar' /></entry>
<entry><a>value 1</a><b foo='baz' /></entry>
<entry><a>value 2</a><b foo='quz' /></entry>
...
</root>
Может обрабатываться следующим образом.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import time
import xml.sax
class StreamHandler(xml.sax.handler.ContentHandler):
lastEntry = None
lastName = None
def startElement(self, name, attrs):
self.lastName = name
if name == 'entry':
self.lastEntry = {}
elif name != 'root':
self.lastEntry[name] = {'attrs': attrs, 'content': ''}
def endElement(self, name):
if name == 'entry':
print({
'a' : self.lastEntry['a']['content'],
'b' : self.lastEntry['b']['attrs'].getValue('foo')
})
self.lastEntry = None
elif name == 'root':
raise StopIteration
def characters(self, content):
if self.lastEntry:
self.lastEntry[self.lastName]['content'] += content
if __name__ == '__main__':
# use default ``xml.sax.expatreader``
parser = xml.sax.make_parser()
parser.setContentHandler(StreamHandler())
# feed the parser with small chunks to simulate
with open('data.xml') as f:
while True:
buffer = f.read(16)
if buffer:
try:
parser.feed(buffer)
except StopIteration:
break
else:
time.sleep(2)
# if you can provide a file-like object it's as simple as
with open('data.xml') as f:
parser.parse(f)