您可以尝试使用类似 SAX 的target parser interface:
from lxml import etree
class SkipStartEndTarget:
def __init__(self, *args, **kwargs):
self.builder = etree.TreeBuilder()
self.skip = False
def start(self, tag, attrib, nsmap=None):
if tag == 'start':
self.skip = True
if not self.skip:
self.builder.start(tag, attrib, nsmap)
def data(self, data):
if not self.skip:
self.builder.data(data)
def comment(self, comment):
if not self.skip:
self.builder.comment(self)
def pi(self, target, data):
if not self.skip:
self.builder.pi(target, data)
def end(self, tag):
if not self.skip:
self.builder.end(tag)
if tag == 'end':
self.skip = False
def close(self):
self.skip = False
return self.builder.close()
然后您可以使用SkipStartEndTarget 类创建parser target,并使用该目标创建自定义XMLParser,如下所示:
parser = etree.XMLParser(target=SkipStartEndTarget())
如果需要,您仍然可以向解析器提供其他解析器选项。然后您可以将此解析器提供给您正在使用的解析器函数,例如:
elem = etree.fromstring(xml_str, parser=parser)
这也适用于etree.XML() 和etree.parse(),您甚至可以使用etree.setdefaultparser() 将解析器设置为默认解析器(这可能不是一个好主意)。一件事可能会让您感到困惑:即使使用etree.parse(),它也不会返回元素树,但始终会返回一个元素(就像etree.XML() 和etree.fromstring() 一样)。我认为这(目前)还不能做到,所以如果这对您来说是个问题,您将不得不以某种方式解决它。
请注意,也可以使用 lxml.sax 从 sax 事件创建元素树,这可能会更困难和更慢。与上面的示例相反,它将返回一个元素树,但我认为它不提供您在正常使用etree.parse() 时会得到的.docinfo。我也相信它(目前)不支持 cmets 和 pi。 (还没用过,暂时不能更准确)
还请注意,任何类似 SAX 的文档解析方法都要求跳过 <start/> 和 <end/> 之间的所有内容仍会生成格式良好的文档,在您的示例中就是这种情况,但不会是例如,如果第二个<p> 是<p2>,那么您最终会得到<p>....</p2>。