<p>我建议使用<code>lxml</code>,因为它在大多数方面比stdlib<code>xml.ElementTree</code>更有效。在</p>
<p>不要试图将整个文档作为一个整体来解析,因为它太大了,而应该迭代地处理源文档。在</p>
<p>在<code>lxml</code>页是<a href="http://lxml.de/tutorial.html#event-driven-parsing" rel="nofollow">Event driven parsing</a></p>
<p>有两种选择:</p>
<ul>
<li><code>etree.iterparse</code></li>
<li>使用自定义解析器,激发类似SAX的事件</li>
</ul>
<p>我个人更喜欢<code>etree.iterparse</code>,因为它以更方便的方式为您解析了元素。但是您不能忘记对处理过的部分执行清理工作,否则与一次解析整个文档相比,您不会节省任何内存。在</p>
<p><strong>编辑:添加真实示例</strong></p>
<p>例子比大量的理论更能说明问题。以下是我的尝试:</p>
<pre><code>from lxml import etree
# fname = "large.xml" # 78 MB
fname = "verylarge.xml" # 773 MB
toremove = []
for event, element in etree.iterparse(fname):
if element.tag == "TableRow":
if element.attrib["RowCount"] != "0":
element.clear()
# removing current element causes segmentation fault
# element.getparent().remove(element)
toremove.append(element)
if element.tag == "TableData":
for rowelm in toremove:
element.remove(rowelm)
toremove = []
# last processed element is the root one
with open("out.xml", "w") as f:
f.write(etree.tostring(element))
</code></pre>
<p>为了测试性能,我取了你的大样本文件(73MB),重复内部部分10次,得到
773 MB的大型XML文件,并已处理该文件。在</p>
<p>处理耗时24秒(ZenbookCoreI7,内存为4GB),结果文件的大小为4.7MB。在</p>
<h2>举例说明</h2>
<p>默认情况下,<code>iterparse</code>只提供“end”事件,当某个元素被完全解析时激发。在</p>
<p>这个解决方案使用了这样一个事实,即即使使用<code>iterparse</code>,元素也会保存在内存中。这是用过的
在以下位置:</p>
<ul>
<li>在iterparse期间,不需要的元素被清除(<code>element.clear()</code>)并被删除
(<code>element.remove(rowelm)</code>)。<code>clear()</code>清除元素的内部内容,但是元素
仍然存在。<code>remove()</code>对父元素起作用,并从中删除内部部分。在</li>
<li>要使用的元素没有被移除和清除,所以我们在最后找到它们
根元素。在</li>
<li>最后,当处理完所有内容时,last processed <code>element</code>是根目录。它还在记忆中,
所以我可以把它作为字符串写入文件。在</li>
</ul>
<p>在<code>remove()</code>元素时必须小心。正在尝试从位于的父级中删除元素
当前迭代元素的瞬间导致分割错误。因此,代码
使用<code>"TableRow"</code>元素<code>remove()</code>等待,直到完成对父元素<code>TableData</code>的解析。在</p>
<p>变量<code>toremove</code>用于收集所有<code>"TableRow"</code>元素,并在父元素后立即使用
<code>"TableData"</code>元素被完全解析。注意,<code>remove()</code>只对实际元素有效
父母,所以我们一定要在适当的时候做。在</p>
<h2>更大文件的想法</h2>
<p>对于更大的文件,此解决方案将受到结果XML文档大小的限制
保存在内存中,直到源XML的修剪完成。在</p>
<p>对于这样的场景,我们必须在解析过程中写出输出,并将
内存中已处理的所有元素。实际上,你必须写下
“开始”事件时的“打开XML元素”部分(如<code>"<TaskReportSummary att="a" otheratt="bb"</code>)
将出现,并在“end”事件中编写关闭XML元素部分<code>"/>"</code>。在</p>