remove
方法:
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)
如果我必须与 @Acorn 版本进行比较,即使要删除的元素不直接位于 xml 的根节点下,我的版本也会工作。
您正在寻找
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)
结果:
<groceries>
<fruit state="fresh">pear</fruit>
<fruit state="fresh">starfruit</fruit>
<fruit state="fresh">peach</fruit>
</groceries>
我遇到过一种情况:
<div>
<script>
some code
</script>
text here
</div>
div.remove(script)
将删除我无意的 text here
部分。
按照here的答案,我发现
etree.strip_elements
对我来说是一个更好的解决方案,您可以通过with_tail=(bool)
参数控制是否删除后面的文本。
但是我仍然不知道这是否可以使用xpath过滤器作为标签。只是为了通知而已。
这是文档:
strip_elements(tree_or_element, *tag_names, with_tail=True)
从树中删除具有提供的标签名称的所有元素或 子树。这将删除元素及其整个子树, 包括它们的所有属性、文本内容和后代。它 还将删除元素的尾部文本,除非您 明确将
关键字参数选项设置为 False。with_tail
标签名称可以包含通配符,如
。_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 )
正如已经提到的,您可以使用
remove()
方法从树中删除(子)元素:
for bad in tree.xpath("//fruit[@state=\'rotten\']"):
bad.getparent().remove(bad)
但是它会删除包括其
tail
在内的元素,如果您正在处理 HTML 之类的混合内容文档,这是一个问题:
<div><fruit state="rotten">avocado</fruit> Hello!</div>
成为
<div></div>
我想这就是你并不总是想要的:) 我创建了辅助函数来仅删除元素并保留其尾部:
def remove_element(el):
parent = el.getparent()
if el.tail.strip():
prev = el.getprevious()
if prev:
prev.tail = (prev.tail or '') + el.tail
else:
parent.text = (parent.text or '') + el.tail
parent.remove(el)
for bad in tree.xpath("//fruit[@state=\'rotten\']"):
remove_element(bad)
这样它将保留尾部文本:
<div> Hello!</div>
您还可以使用 lxml 中的 html 来解决这个问题:
from lxml import html
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 = html.fromstring(xml)
print("//BEFORE")
print(html.tostring(tree, pretty_print=True).decode("utf-8"))
for i in tree.xpath("//fruit[@state='rotten']"):
i.drop_tree()
print("//AFTER")
print(html.tostring(tree, pretty_print=True).decode("utf-8"))
它应该输出这个:
//BEFORE
<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>
//AFTER
<groceries>
<fruit state="fresh">pear</fruit>
<fruit state="fresh">starfruit</fruit>
<fruit state="fresh">peach</fruit>
</groceries>
remove
函数从树中分离元素,因此删除 XML 节点(元素、PI 或注释)、其内容(后代项)和 tail
文本。在这里,保留 tail
文本是多余的,因为它只包含空格和换行符,可以将其视为可忽略的空格。
要删除元素(及其内容),保留其
tail
,您可以使用以下函数:
def remove_node(child, keep_content=False):
"""
Remove an XML element, preserving its tail text.
:param child: XML element to remove
:param keep_content: ``True`` to keep child text and sub-elements.
"""
parent = child.getparent()
parent_text = parent.text or u""
prev_node = child.getprevious()
if keep_content:
# insert: child text
child_text = child.text or u""
if prev_node is None:
parent.text = u"{0}{1}".format(parent_text, child_text) or None
else:
prev_tail = prev_node.tail or u""
prev_node.tail = u"{0}{1}".format(prev_tail, child_text) or None
# insert: child elements
index = parent.index(child)
parent[index:index] = child[:]
# insert: child tail
parent_text = parent.text or u""
prev_node = child.getprevious()
child_tail = child.tail or u""
if prev_node is None:
parent.text = u"{0}{1}".format(parent_text, child_tail) or None
else:
prev_tail = prev_node.tail or u""
prev_node.tail = u"{0}{1}".format(prev_tail, child_tail) or None
# remove: child
parent.remove(child)
这是一个演示:
from lxml import etree
tree = etree.XML(u"<root>text <bad>before <bad>inner</bad> after</bad> tail</root>")
bad1 = tree.xpath("//bad[1]")[0]
remove_node(bad1)
etree.dump(tree)
# <root>text tail</root>
如果您想保留内容,您可以这样做:
tree = etree.XML(u"<root>text <bad>before <bad>inner</bad> after</bad> tail</root>")
bad1 = tree.xpath("//bad[1]")[0]
remove_node(bad1, keep_content=True)
etree.dump(tree)
# <root>text before <bad>inner</bad> after tail</root>