我正在尝试使用python&lxml删除2个标签之间的XML文档中的所有内容。问题在于标签可能位于树的不同分支中(但始终在同一深度),示例文档可能看起来像这样。
<root>
<p> Hello world <start />this is a paragraph </p>
<p> Goodbye world. <end />I'm leaving now </p>
</root>
我想删除开始和结束标签之间的所有内容。这将导致单个p标签:
<root>
<p> Hello world I'm leaving now </p>
</root>
有谁知道如何使用lxml和python实现此目的?
最佳答案
您可以尝试使用类似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事件创建elementtree,这可能会更困难,更慢。与上面的示例相反,它将返回一个元素树,但是我认为它不提供通常使用
.docinfo
时会得到的etree.parse()
。我也认为(当前)不支持注释和pi。 (尚未使用过,因此目前无法更精确)还要注意,任何类似于SAX的解析文档的方法都要求跳过
<start/>
和<end/>
之间的所有内容仍将生成格式正确的文档,在您的示例中就是这种情况,但如果例如,第二个<p>
是<p2>
,因为您最终会得到<p>....</p2>
。