为啥 XPath contains(text(),'substring') 没有按预期工作?
Posted
技术标签:
【中文标题】为啥 XPath contains(text(),\'substring\') 没有按预期工作?【英文标题】:Why is XPath contains(text(),'substring') not working as expected?为什么 XPath contains(text(),'substring') 没有按预期工作? 【发布时间】:2021-12-22 20:15:38 【问题描述】:假设我有一段这样的 html:
<a><other/>more text</a>
我可以匹配这块XPath:
//a[text() = '']
或者……
//a[text() = 'more text']
或者我可以用点来匹配整个东西:
//a[. = 'more text']
This post 描述了.
(点)和text()
之间的这种区别,但简而言之,第一个返回单个元素,而后者返回一个元素列表。但这对我来说有点奇怪。因为虽然text()
可用于匹配列表中的任一元素,但在涉及XPath 函数contains()
时情况并非如此。如果我这样做:
//a[contains(text(), '')]
...我收到以下错误:
错误: contains() 的第一个参数的必需基数是一或零
text()
怎么会在使用完全匹配(等于)时有效,但对部分匹配(包含)无效?
【问题讨论】:
【参考方案1】:对于这个标记,
<a><other/>more text</a>
请注意,a
元素有一个文本节点子节点 (""
)、一个空元素子节点 (other
) 和第二个文本节点子节点 ("more text"
)。
以下是根据该标记评估 //a[contains(text(),'')]
时发生的情况的推理方法:
contains(x,y)
期望 x
是一个字符串,但 text()
匹配两个文本节点。
在XPath 1.0中,将多个节点转换为字符串的规则是this:
通过返回string-value 将节点集转换为字符串 document order 中 first 的节点集中的节点。如果 node-set 为空,返回一个空字符串。 [强调补充]
- 在 XPath 2.0+ 中,向需要字符串的函数提供一系列文本节点是错误的,因此
contains(text(),'substr')
会导致多个匹配文本节点出错。
你的情况……
XPath 1.0 会将contains(text(),'')
视为
contains('','')
这是true
。另一方面,一定要注意contains(text(),'more text')
在 XPath 1.0 中的计算结果为false
。在不了解上述 (1)-(3) 的情况下,这可能是违反直觉的。
XPath 2.0 会将其视为错误。
更好的选择
如果目标是查找字符串值包含子字符串的所有a
元素,""
:
//a[contains(.,'')]
这是最常见的要求。
如果目标是查找所有a
元素,其直接文本节点子节点等于""
:
//a[text()='']
这在希望从a
中的后代元素中排除字符串时很有用,例如如果你想要这个a
,
<a><other/>more text</a>
但不是这个a
:
<a>more text before <not></not> more text after</a>
另见
Howcontains()
handles a nodeset first arg
How to use XPath contains() for specific text?
Testing text() nodes vs string values in XPath
【讨论】:
答案的宝石。清除所有疑虑 (+1)。【参考方案2】:原因是contains
函数不接受节点集作为输入——它只接受字符串。 (嗯,它可能依赖于引擎,因为它适用于Python
的lxml
模块。根据规范,它应该将集合中第一个节点的值转换为字符串并对其进行操作。另见XPath contains(text(),'some string') doesn't work when used with node with more than one Text subnode)
//a[text() = '']
匹配任何包含等于 的文本节点的
a
元素。
//a[text() = 'more text']
匹配任何包含等于more text
的文本节点的a
元素。
所以这两个表达式都匹配相同的a
元素。
您可以将查询重新处理为//a[text()[contains(., '')]]
,以便contains
方法一次只作用于一个文本节点。
【讨论】:
你写了“所以这两个表达式都匹配同一个 a 元素。”但是字符串''
和'more text'
并不相同,那么如何将a
元素与文本EQUALS 匹配到'' 并将a
元素与文本EQUALS 匹配到'更多文本' 匹配相同的元素?这打破了一些基本逻辑!!!我认为这是可行的,但我仍然不清楚。以上是关于为啥 XPath contains(text(),'substring') 没有按预期工作?的主要内容,如果未能解决你的问题,请参考以下文章
与具有多个文本子节点的节点一起使用时,XPath contains(text(),'some string') 不起作用