【问题标题】:How to prevent lxml remove method from removing text between two elements如何防止 lxml remove 方法删除两个元素之间的文本
【发布时间】:2016-08-31 00:00:14
【问题描述】:

我正在使用 lxml 和 python 2.7 来解析 xml 文件。我需要在某些时候使用 remove 方法来删​​除一个元素,但很奇怪的是它也会删除它之后的一些文本。

输入的xml是:

<ce:para view="all">Web and grid services <ce:cross-refs refid="BIB10 BIB11">[10,11]</ce:cross-refs>, where they can provide rich service descriptions that can help in locating suitable services.</ce:para>

然后我需要将cross-refs 元素扩展为多个cross-ref,并分隔refid。所以输出应该是这样的:

<ce:para view="all">Web and grid services <ce:cross-ref refid="BIB10">[10]</ce:cross-ref><ce:cross-ref refid="BIB11">[11]</ce:cross-ref>, where they can provide rich service descriptions that can help in locating suitable services.</ce:para>

这是带有一些缩写的python代码:

xpath = "//ce:cross-refs"
cross_refs = tree.xpath(xpath, namespaces={'ce': 'http://www.elsevier.com/xml/common/dtd'})
for c in cross_refs:
    c_parent = c.getparent()
    c_values = c.text.strip("[]")
    ...
    ref_ids = c.attrib['refid'].strip().split()
    i = 0
    for r in ref_ids:
        ...
        tag = et.QName(CE, 'cross-ref')
        exploded_cross_refs = et.Element(tag, refid=r, nsmap=NS_MAP)
        exploded_cross_refs.text = "[" + c_values[i] + "]"
        c.addprevious(exploded_cross_refs)
        i += 1
    c_parent.remove(c)

获取cross-refs 元素,展开refid 值和元素文本值,然后创建新的cross-ref 元素并将它们添加到原始cross-refs 之前,最后我想删除旧的cross-refs 元素和我的问题就在这里:当我删除这个元素时,结束标记和下一个元素之间的文本也会被删除,所以最终结果是这样的:

<ce:para view="all">Web and grid services <ce:cross-ref refid="BIB10">[10]</ce:cross-ref><ce:cross-ref refid="BIB11">[11]</ce:cross-ref></ce:para>

请注意,最后一个 cross-refpara 元素之间的文本已被删除!我该如何解决这个问题?

【问题讨论】:

    标签: python xml python-2.7 lxml


    【解决方案1】:

    或者,特别是如果不是某个父项中某个名称的所有元素都需要删除,我们可以创建简单的方法,将尾部附加到前一个元素(如果有的话),或者附加它否则,在元素实际被删除之前:

    def remove_preserve_tail(element):
        if element.tail:
            prev = element.getprevious()
            parent = element.getparent()
            if prev is not None:
                prev.tail = (prev.tail or '') + element.tail
            else:
                parent.text = (parent.text or '') + element.tail
        parent.remove(element)
    

    演示:

    >>> from lxml import etree
    >>> raw = '''<root>
    ... foo
    ... <div></div>has tail and no prev
    ... <br/><div></div>has tail and prev
    ... <br/>
    ... <div>no tail, whitespaces only</div>
    ... </root>'''
    ... 
    >>> root = etree.fromstring(raw)
    >>> divs = root.xpath("//div")
    >>> for div in divs:
    ...     remove_preserve_tail(div)
    ... 
    >>> print etree.tostring(root)
    <root>
    foo
    has tail and no prev
    <br/>has tail and prev
    <br/>
    
    </root>
    

    【讨论】:

    • 我想在我的情况下,我需要删除所有 corss-refs 元素,strip_elements 使用更简洁的代码运行得更快。对吗?
    • 是的,您的方法可能会更快,并且肯定有更简洁的代码。对于这种特殊情况,我也会采用您的方法。
    【解决方案2】:

    看来remove 方法,默认删除element.tail。所以我将remove 替换为strip_elements 方法,该方法采用with_tail 参数,因此您可以控制是否删除尾部。这是documentation,这是我使用的命令:

    et.strip_elements(c_parent, 'cross-refs', with_tail=False)
    

    【讨论】:

      猜你喜欢
      • 2013-05-11
      • 1970-01-01
      • 2020-10-12
      • 1970-01-01
      • 1970-01-01
      • 2015-05-17
      • 2014-09-27
      • 2011-12-20
      • 1970-01-01
      相关资源
      最近更新 更多