Python 如何用lxml中的文本替换元素?

Python 如何用lxml中的文本替换元素?,python,xml,lxml,elementtree,Python,Xml,Lxml,Elementtree,使用lxml的ElementTreeAPI实现可以很容易地从XML文档中完全删除给定的元素,但是我看不到一种简单的方法来一致地用一些文本替换元素。例如,给定以下输入: input = '''<everything> <m>Some text before <r/></m> <m><r/> and some text after.</m> <m><r/></m> <m>

使用lxml的ElementTreeAPI实现可以很容易地从XML文档中完全删除给定的元素,但是我看不到一种简单的方法来一致地用一些文本替换元素。例如,给定以下输入:

input = '''<everything>
<m>Some text before <r/></m>
<m><r/> and some text after.</m>
<m><r/></m>
<m>Text before <r/> and after</m>
<m><b/> Text after a sibling <r/> Text before a sibling<b/></m>
</everything>
'''
但是,如何用文本替换每个元素以获得输出:

<everything>
<m>Some text before DELETED</m>
<m>DELETED and some text after.</m>
<m>DELETED</m>
<m>Text before DELETED and after</m>
<m><b/>Text after a sibling DELETED Text before a sibling<b/></m>
</everything>

删除之前的一些文本
删除和一些文字后。
删除
删除前和删除后的文本
同级之后的文本删除同级之前的文本
在我看来,由于ElementTree API通过每个元素的
.text
.tail
属性而不是树中的节点来处理文本,这意味着您必须根据元素是否具有同级元素来处理许多不同的情况,现有元素是否具有
.tail
属性,依此类推。我错过了一些简单的方法吗?

使用:

导入io
将lxml.etree作为ET导入
数据=“”
之前的一些文本
之后还有一些文字。
前后文本
兄弟姐妹后的文本兄弟姐妹前的文本
'''
f=ET.fromstring(数据)
xslt=''\
删除
'''
xslt_doc=ET.parse(io.BytesIO(xslt))
transform=ET.XSLT(XSLT\u doc)
f=变换(f)
打印(等至字符串(f))
屈服

<everything>
<m>Some text before DELETED</m>
<m>DELETED and some text after.</m>
<m>DELETED</m>
<m>Text before DELETED and after</m>
<m><b/> Text after a sibling DELETED Text before a sibling<b/></m>
</everything>

删除之前的一些文本
删除和一些文字后。
删除
删除前和删除后的文本
同级之后的文本删除同级之前的文本

我认为unutbu的XSLT解决方案可能是实现目标的正确方法

然而,这里有一种有点老套的方法来实现它,方法是修改
标记的尾部,然后使用
etree.strip\u元素

从lxml导入etree
数据=“”
之前的一些文本
之后还有一些文字。
前后文本
兄弟姐妹后的文本兄弟姐妹前的文本
'''
f=etree.fromstring(数据)
对于f.xpath('//r')中的r:
r、 tail='DELETED'+r.tail如果r.tail其他'DELETED'
etree.strip_元素(f,'r',带_tail=False)
打印etree.tostring(f,pretty\u print=True)
给你:

<everything>
<m>Some text before DELETED</m>
<m>DELETED and some text after.</m>
<m>DELETED</m>
<m>Text before DELETED and after</m>
<m><b/> Text after a sibling DELETED Text before a sibling<b/></m>
</everything>

删除之前的一些文本
删除和一些文字后。
删除
删除前和删除后的文本
同级之后的文本删除同级之前的文本

使用
strip\u元素
的缺点是,您无法使它在替换其他元素时保留部分
元素。它还需要存在
ElementTree
实例(可能不是这样)。最后,您不能使用它来替换XML注释或处理指令。 以下是你应该做的工作:


如果
有子项,是否也要删除这些子项?或者合并到
的父节点中?在本例中,我只想删除
节点及其所有子节点,并用文本字符串替换它。希望这更容易:)+1这是一个很好的,但确实不明显的答案:)我想到这个问题是因为我的能力不足,我希望有一个比这个更简单的方法。即使有这样一个简短的例子,XSLT也很冗长,与我问题中删除元素的代码相比,很难理解。谢谢,这是一个很好的解决方案-我不知道
strip\u elements
with\u tail
选项,我不愿意使用lxml进行html处理。但可能会切换到Beautifulsoup,它对基本html修改更加直观,并且可以使用lxml作为解析器
soup=BeautifulSoup(文本,“lxml”)/soup。查找所有('r')。将所有('u替换为('DELETED')
感谢@benzkij提供的提示!非常奇怪的是,文本有时被视为ElementTree API中其他节点的尾部,而不仅仅是xml想要的普通文本节点。我认为
text='DELETED'+r.tail
应该是
text='DELETED'+r.tail,如果r.tail是'DELETED'
<everything>
<m>Some text before DELETED</m>
<m>DELETED and some text after.</m>
<m>DELETED</m>
<m>Text before DELETED and after</m>
<m><b/> Text after a sibling DELETED Text before a sibling<b/></m>
</everything>
<everything>
<m>Some text before DELETED</m>
<m>DELETED and some text after.</m>
<m>DELETED</m>
<m>Text before DELETED and after</m>
<m><b/> Text after a sibling DELETED Text before a sibling<b/></m>
</everything>
for r in f.xpath('//r'):
    text = 'DELETED' + r.tail 
    parent = r.getparent()
    if parent is not None:
        previous = r.getprevious()
        if previous is not None:
            previous.tail = (previous.tail or '') + text
        else:
            parent.text = (parent.text or '') + text
        parent.remove(r)