如何在LXML中使用iterparse()正确处理文本元素?

时间:2018-09-24 08:17:29

标签: xml python-3.x lxml sax

这是一个简单的XML文件:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<root>
  <someThing>
    Text A: This is a test line.
    <p>Some paragraph.</p>
    Text B: This is another test line.
    <p>Some other paragraph.</p>
    Text C: And even another test line.
  </someThing>
</root>

尽管测试XML文件非常小,但我要处理的实际文件却相当大:几GB。因此,我想使用以下Python代码使用iterparse()解析此文件。

import xml.etree.ElementTree as etree
FILE_NAME = "test.xml"
for event, element in etree.iterparse(FILE_NAME, events=("start", "end", "start-ns", "end-ns")):
        print(event, "\t", element, "\t", repr(element.text))

如果运行此命令,则会得到以下输出:

start    <Element 'root' at 0x7fd96a6aa728>      '\n\t'
start    <Element 'someThing' at 0x7fd968fba688>     '\n\t\tText A: This is a test line.\n\t\t'
start    <Element 'p' at 0x7fd968fcaf48>     'Some paragraph.'
end      <Element 'p' at 0x7fd968fcaf48>     'Some paragraph.'
start    <Element 'p' at 0x7fd968fcaf98>     'Some other paragraph.'
end      <Element 'p' at 0x7fd968fcaf98>     'Some other paragraph.'
end      <Element 'someThing' at 0x7fd968fba688>     '\n\t\tText A: This is a test line.\n\t\t'
end      <Element 'root' at 0x7fd96a6aa728>      '\n\t'

您会看到<p>元素之后的文本元素被忽略。

我的问题是:我该如何使用LXML API正确处理此文件的内容?到目前为止,我能找到的与该主题有关的所有示例都与我的短代码没什么不同,因此也遇到了相同的问题。如果无法使用LXML做到这一点,那么有人知道我可以使用其他XML解析器吗,并提供一个简短的示例吗?

1 个答案:

答案 0 :(得分:0)

根据用户mzjn的友好注释,可以使用LXML进行操作:有一个tail属性,缺少的文本将附加到该属性。但这可能不是最佳方法,原因如下:

  • LXML不可避免地会构造一棵大树(当然,在处理事件期间,可以部分地将其截断,但这远非最佳选择)
  • 没有用于解析两个元素之间的文本的干净事件,因此您必须通过解析tail来解决此问题。

替代解决方案:直接使用SAX API。

示例:

import xml.sax

FILE_NAME = "test.xml"

class MyHandler(xml.sax.handler.ContentHandler):

    def startElement(self, name, attrs):
        print("startElement\t" + repr(name))

    def endElement(self, name):
        print("endElement\t" + repr(name))

    def startElementNS(self, name, qname, attrs):
        print("startElementNS\t" + repr(name))

    def endElementNS(self, name, qname):
        print("endElementNS\t" + repr(name))

    def characters(self, content):
        print("  chars\t\t" + repr(content))

contentHandler = MyHandler()
xml.sax.parse(FILE_NAME, contentHandler)

上面的示例将是处理大型XML文件的一个很好的起点。方法characters()将为每个已解析的文本调用。实体被解码并导致对characters()的调用,因此很容易收集存储在XML元素上的所有文本。如果您实现了一个简单的堆栈,该堆栈在内容处理程序中对startElement()endElement()的调用上会增加和减少,则可以轻松解析树的特定部分,而无需处理。< / p>