如何防止lxml remove方法删除两个元素之间的文本

时间:2016-05-05 09:03:01

标签: python xml python-2.7 lxml

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

输入xml是:

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> 。所以输出应该是这样的:

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)

这里是python代码的缩写:

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-ref

请注意,上一个para和{{1}}元素之间的文字已被删除!我该如何解决这个问题?

2 个答案:

答案 0 :(得分:1)

似乎是remove方法,默认情况下会移除element.tail。所以我用remove方法替换strip_elements,该方法采用with_tail参数,因此您可以控制是否删除尾部。这是documentation,这是我使用的命令:

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

答案 1 :(得分: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>