如何删除lxml中的元素

ewo*_*wok 79 python xml lxml

我需要使用python的lxml基于属性的内容完全删除元素.例:

import lxml.etree as et

xml="""
<groceries>
  <fruit state="rotten">apple</fruit>
  <fruit state="fresh">pear</fruit>
  <fruit state="fresh">starfruit</fruit>
  <fruit state="rotten">mango</fruit>
  <fruit state="fresh">peach</fruit>
</groceries>
"""

tree=et.fromstring(xml)

for bad in tree.xpath("//fruit[@state=\'rotten\']"):
  #remove this element from the tree

print et.tostring(tree, pretty_print=True)
Run Code Online (Sandbox Code Playgroud)

我想要打印:

<groceries>
  <fruit state="fresh">pear</fruit>
  <fruit state="fresh">starfruit</fruit>
  <fruit state="fresh">peach</fruit>
</groceries>
Run Code Online (Sandbox Code Playgroud)

有没有办法在不存储临时变量并手动打印的情况下执行此操作,如下所示:

newxml="<groceries>\n"
for elt in tree.xpath('//fruit[@state=\'fresh\']'):
  newxml+=et.tostring(elt)

newxml+="</groceries>"
Run Code Online (Sandbox Code Playgroud)

Céd*_*ien 138

使用removexmlElement 的方法:

tree=et.fromstring(xml)

for bad in tree.xpath("//fruit[@state=\'rotten\']"):
  bad.getparent().remove(bad)     # here I grab the parent of the element to call the remove directly on it

print et.tostring(tree, pretty_print=True, xml_declaration=True)
Run Code Online (Sandbox Code Playgroud)

如果我必须与@Acorn版本进行比较,即使要删除的元素不直接位于xml的根节点下,我的工作也会正常工作.


Aco*_*orn 28

你正在寻找这个remove功能.调用树的remove方法并传递一个子元素来删除.

import lxml.etree as et

xml="""
<groceries>
  <fruit state="rotten">apple</fruit>
  <fruit state="fresh">pear</fruit>
  <punnet>
    <fruit state="rotten">strawberry</fruit>
    <fruit state="fresh">blueberry</fruit>
  </punnet>
  <fruit state="fresh">starfruit</fruit>
  <fruit state="rotten">mango</fruit>
  <fruit state="fresh">peach</fruit>
</groceries>
"""

tree=et.fromstring(xml)

for bad in tree.xpath("//fruit[@state='rotten']"):
    bad.getparent().remove(bad)

print et.tostring(tree, pretty_print=True)
Run Code Online (Sandbox Code Playgroud)

结果:

<groceries>
  <fruit state="fresh">pear</fruit>
  <fruit state="fresh">starfruit</fruit>
  <fruit state="fresh">peach</fruit>
</groceries>
Run Code Online (Sandbox Code Playgroud)

  • @ewok:给Cédric接受,因为他比我早1秒回答**,更重要的是,他的回答是正确的:) (15认同)
  • 啊,我忽略了`.remove()`要求元素是你调用它的元素的子元素这一事实.因此,您需要在要删除的元素的父级上调用它.答案已更正. (3认同)
  • @Acorn以这种态度,请投票! (2认同)

zep*_*hor 12

我遇到了一种情况:

<div>
    <script>
        some code
    </script>
    text here
</div>
Run Code Online (Sandbox Code Playgroud)

div.remove(script)将删除text here我不是故意的部分.

按照这里的答案,我发现etree.strip_elements对我来说这是一个更好的解决方案,你可以控制是否用with_tail=(bool)param 删除后面的文本.

但我仍然不知道这是否可以使用xpath过滤器标记.只是把它作为通知.

这是文档:

strip_elements(tree_or_element,*tag_names,with_tail = True)

使用树或子树中提供的标记名称删除所有元素.这将删除元素及其整个子树,包括其所有属性,文本内容和后代.除非您将with_tail关键字参数选项显式设置为False,否则它还将删除元素的尾部文本.

标记名称可以包含通配符,如_Element.iter.

请注意,即使匹配,也不会删除您传递的元素(或ElementTree根元素).它只会对待它的后代.如果要包含根元素,请在调用此函数之前直接检查其标记名称.

用法示例::

   strip_elements(some_element,
       'simpletagname',             # non-namespaced tag
       '{http://some/ns}tagname',   # namespaced tag
       '{http://some/other/ns}*'    # any tag from a namespace
       lxml.etree.Comment           # comments
       )
Run Code Online (Sandbox Code Playgroud)


Mes*_*ssa 5

正如已经提到的,您可以使用该remove()方法从树中删除(子)元素:

for bad in tree.xpath("//fruit[@state=\'rotten\']"):
  bad.getparent().remove(bad)
Run Code Online (Sandbox Code Playgroud)

但它会删除包括其 在内的元素tail,如果您正在处理 HTML 等混合内容文档,这会是一个问题:

<div><fruit state="rotten">avocado</fruit> Hello!</div>
Run Code Online (Sandbox Code Playgroud)

成为

<div></div>
Run Code Online (Sandbox Code Playgroud)

我想这就是你并不总是想要的:)我创建了辅助函数来仅删除元素并保留其尾部:

for bad in tree.xpath("//fruit[@state=\'rotten\']"):
  bad.getparent().remove(bad)
Run Code Online (Sandbox Code Playgroud)

这样它将保留尾部文本:

<div> Hello!</div>
Run Code Online (Sandbox Code Playgroud)