【问题标题】:remove everything between 2 tags that span branches of an xml tree删除跨越 xml 树分支的 2 个标签之间的所有内容
【发布时间】:2010-08-04 01:20:44
【问题描述】:

我正在尝试使用 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 来实现吗?

【问题讨论】:

    标签: python xml lxml


    【解决方案1】:

    你手上的东西一团糟,应该揍那个故意歪曲 XML 嵌套规则的人。

    您可能最好使用SAX 之类的东西来识别&lt;start/&gt; 标签并开始丢弃输入,直到您遇到&lt;end/&gt;。 SAX 在这方面比 lxml 有优势,因为它允许您对每个词位采取任意操作,而 lxml 在您接触它们之前已经分离了开始和结束。

    当您使用它时,您可能希望将这些文档转换为可用的 XML。

    【讨论】:

    • 哦,我多么希望我能做点什么……这是一个 ODT 文件。他们使用这些来“跟踪更改”,不幸的是我正在使用 etree 对文件进行很多其他操作,所以我不确定是否可以切换到 SAX :( 很高兴知道它会处理它,我可能会需要调查一下。
    【解决方案2】:

    我知道有些人会因此而对我下手,但你可以使用正则表达式:

    import re
    new_string = re.sub(r'<start />(.*?)<end />', '', your_string, re.S)
    

    当 XML 解析器不是有效的 XML 时,您不能使用它。

    【讨论】:

    • 我倾向于同意。虽然这在某些情况下可能效率较低,但它可能不会造成那么大的性能问题。
    • XML 完全有效。请注意,开始和结束是完整的自闭合标签。我已经考虑过正则表达式路线,但是文档很大,我需要删除很多这种情况。
    • @user61 你是对的,它是有效 XML。我不知道更好的词。这不是“正确的”XML,也许吧?无论如何,如果您可以在 slurp 模式下阅读它,那么无论大小如何,您都可能没问题。
    • @NullUserException 看起来这是我必须采取的路线,但在同一个脚本中,我对文档进行了很多其他操作,我不确定它是如何操作的将有助于以“啜饮”模式完成,这是我以前从未使用过的。
    • @NullUserException,对不起,我以为您指的是使用 SAX 或类似的东西。我对正则表达式方法的唯一问题是我有一个操作列表(插入和删除),我需要对需要以正确顺序应用的文档执行这些操作,所以我必须转储整个文档到一个字符串,并在每次删除时重新解析它的全部内容,其中一个文档中可能有数百个。
    【解决方案3】:

    您可以尝试使用类似 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 的文档解析方法都要求跳过 &lt;start/&gt;&lt;end/&gt; 之间的所有内容仍会生成格式良好的文档,在您的示例中就是这种情况,但不会是例如,如果第二个&lt;p&gt;&lt;p2&gt;,那么您最终会得到&lt;p&gt;....&lt;/p2&gt;

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-12-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-11
      • 1970-01-01
      相关资源
      最近更新 更多