如何用lxml提取HTML文本?断言失败原因及文本修改方法
lxml 相关问题答疑
1. 用lxml提取HTML片段中的文本
提取文本的方式取决于你需要的范围,这里给你几种常用的方法:
- 提取元素的直接文本:使用
element.text,但注意它只返回元素起始标签到第一个子元素(或结束标签)之间的文本。如果元素第一个子节点是其他元素,element.text会返回None。 - 提取元素下所有文本(包括子节点):用XPath的
string()函数,它会拼接元素下所有文本内容。 - 迭代所有文本节点:使用
element.itertext(),可以遍历元素及其子元素的所有文本节点,适合批量提取。
举个实际代码例子:
from lxml import etree html_snippet = '<div><h2><img />XYZZY</h2><p>Hello <b>World</b></p></div>' root = etree.fromstring(html_snippet) # 获取h2的直接文本(结果是None,因为第一个子节点是img) h2_element = root.find('.//h2') print(h2_element.text) # 输出: None # 获取h2下的所有文本内容 print(h2_element.xpath('string()')) # 输出: "XYZZY" # 迭代整个片段的所有文本 for text in root.itertext(): print(text.strip()) # 依次输出: XYZZY, Hello, World
2. 断言失败的原因及修改"XYZZY"的方法
为什么断言会失败?
先看你的代码:你用XPath.//*[contains(text(),'XYZZY')]匹配到了<h2>元素,但el.text返回None,这是因为:
element.text的定义是元素起始标签到第一个子元素之间的文本,而你的<h2>里第一个子节点是<img>元素,起始标签到<img>之间没有文本,所以h2.text就是None。- 真正的"XYZZY"是
<img>元素的tail属性(子元素结束标签到父元素下一个内容之间的文本),或者说是<h2>的文本子节点,并不是<h2>自身的text属性。
你的XPath之所以能匹配到<h2>,是因为contains(text(), 'XYZZY')中的text()会返回当前元素的所有文本子节点,<h2>确实有一个内容为"XYZZY"的文本子节点,所以匹配成功,但这个文本不属于<h2>的text属性。
如何获取并修改"XYZZY"为"ZYX"?
最直接的方式是直接定位到文本节点本身,然后修改它的内容:
from lxml import etree s = '<div><h2><img />XYZZY</h2></div>' root = etree.fromstring(s) # 定位到包含"XYZZY"的文本节点 text_nodes = root.xpath(".//text()[contains(., 'XYZZY')]") for node in text_nodes: node.text = 'ZYX' # 修改文本内容 # 输出修改后的HTML print(etree.tostring(root, encoding='unicode')) # 结果: <div><h2><img/>ZYX</h2></div>
如果你想通过<h2>元素来修改,也可以遍历它的子节点找到对应的tail:
h2_element = root.find('.//h2') # 遍历h2的子元素,找到带有目标文本的tail for child in h2_element.iterchildren(): if child.tail and 'XYZZY' in child.tail: child.tail = child.tail.replace('XYZZY', 'ZYX')
内容的提问来源于stack exchange,提问作者Watusimoto
相关产品推荐
相关产品推荐

