为啥 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>

另见

How contains() handles a nodeset first arg How to use XPath contains() for specific text? Testing text() nodes vs string values in XPath

【讨论】:

答案的宝石。清除所有疑虑 (+1)。【参考方案2】:

原因是contains 函数不接受节点集作为输入——它只接受字符串。 (嗯,它可能依赖于引擎,因为它适用于Pythonlxml 模块。根据规范,它应该将集合中第一个节点的值转换为字符串并对其进行操作。另见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 匹配这个文本节点?

学习 APPIUM 元素定位 (JAVA)-xpath

与具有多个文本子节点的节点一起使用时,XPath contains(text(),'some string') 不起作用

xpath常用函数

xpath和contains模糊匹配

无法使用xpath follow-sibling获取元素