谁能解释为什么这个片段在断言上失败?
因为文中的<h2>
元素由 lxml 存储在该元素的子元素之一中h2
元素。您可以使用itertext()得到你正在寻找的东西。
from lxml import etree
s = '<div><h2><img />XYZZY</h2></div>'
root = etree.fromstring(s)
elements = root.xpath(".//*[contains(text(),'XYZZY')]")
for el in elements:
el_text = ''.join(el.itertext())
assert el_text is not None
print(el_text)
更新:进一步查看后,发现每个元素都有 3 个相关属性:.tag
, .text
and .tail
.
For the .tail
财产,教程中有一小部分这解释了它:
<html><body>Hello<br/>World</body></html>
在这里,
标签被文本包围。这通常被称为
文档样式或混合内容 XML。元素通过它们的支持这一点
尾部财产。它包含直接跟在元素后面的文本,
直到 XML 树中的下一个元素
How .tail
正在填充的是这里再次解释:
LXML 附加尾随文本,该文本未包装在其自己的标签内,因为.tail
之前标签的属性。
所以我们实际上可以编写以下代码,遍历元素树中的每个元素并找到文本所在的位置XYZZY
位于:
from lxml import etree
s = '<div><h2><img />XYZZY</h2></div>'
root = etree.fromstring(s)
context = etree.iterwalk(root, events=("start","end"))
for action, elem in context:
print("%s: %s : [text=%s : tail=%s]" % (action, elem.tag, elem.text, elem.tail))
Output:
start: div : [text=None : tail=None]
start: h2 : [text=None : tail=None]
start: img : [text=None : tail=XYZZY]
end: img : [text=None : tail=XYZZY]
end: h2 : [text=None : tail=None]
end: div : [text=None : tail=None]
所以它位于.tail
的财产<img>
元素。
关于你的第二个问题:
然后...我怎样才能访问“XYZZY”并将其更改为“ZYX”?
一种解决方案是仅遍历元素树,检查每个元素的文本或尾部是否包含该字符串,然后替换它:
#!/usr/bin/python3
from lxml import etree
s = '<div><h2><img />XYZZY</h2></div>'
root = etree.fromstring(s)
search_string = "XYZZY"
replace_string = "ZYX"
context = etree.iterwalk(root, events=("start","end"))
for action, elem in context:
if elem.text and elem.text.strip() == search_string:
elem.text = replace_string
elif elem.tail and elem.tail.strip() == search_string:
elem.tail = replace_string
print(etree.tostring(root).decode("utf-8"))
Output:
<div><h2><img/>ZYX</h2></div>