我正在尝试使用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>

10-08 05:12