如何匹配包含特定字符串的属性?


442

当属性包含多个单词时,按属性选择节点时遇到问题。例如:

<div class="atag btag" />

这是我的xpath表达式:

//*[@class='atag']

该表达式适用于

<div class="atag" />

但不适用于前面的示例。如何选择<div>


9
我认为值得指出的是,“ atag btag”是单个属性,而不是两个。您正在尝试在xpath中进行子字符串匹配。
skaffman

3
是的,您是对的-那就是我想要的。
crazyrails


1
这就是为什么您应该使用CSS选择器... div.atag或的原因div.btag。超级简单,不匹配字符串,并且WAY更快(并且在浏览器中更好地支持)。XPath(针对HTML)应该归结为有用的……通过包含的文本查找元素以及进行DOM导航。
JeffC

Answers:


485

这是一个示例,该示例查找其className包含的div元素atag

//div[contains(@class, 'atag')]

这是一个示例,该示例查找其className包含atag和的div元素btag

//div[contains(@class, 'atag') and contains(@class ,'btag')]

但是,它还会找到部分匹配项,例如class="catag bobtag"

如果您不希望部分匹配,请参阅下面的bobince答案。


123
@Redbeard:这是字面上的答案,但通常不是类匹配解决方案的目标。特别是,它将匹配<div class="Patagonia Halbtagsarbeit">,其中包含目标字符串,但不是具有给定类的div。
bobince 2011年

3
这将适用于简单的场景-但是请注意,如果要在更广泛的上下文中使用此答案,而对要检查的属性值的控制较少或没有控制权。正确的答案是bobince。
奥利弗·

16
抱歉,这与课程不匹配,与子字符串匹配
Timo Huovinen 2014年

5
它也发现了一个明显的错误:<div class =“ annatag bobtag”>它不应该。
Alexei Vinogradov

6
问题是“包含某个字符串”而不是“匹配某个类”
Alsatian

303

mjv的答案是一个好的开始,但是如果atag不是列出的第一个类名,它将失败。

通常的方法比较笨拙:

//*[contains(concat(' ', @class, ' '), ' atag ')]

只要类仅由空格(而不是其他形式的空格)分隔,此方法就起作用。几乎总是这样。如果不是这样,则必须使它更加笨拙:

//*[contains(concat(' ', normalize-space(@class), ' '), ' atag ')]

(通过类名分隔的字符串进行选择是一种常见的情况,令人惊讶的是,没有像CSS3的'[class〜=“ atag”]'这样的特定XPath函数。)


57
h,xpath需要一些修复
Randy L 2010年

13
如果您不想选择类似“ atagnumbertwo”的css类,则@Redbeard supra123的答案是有问题的,尽管我承认这不太可能(:
drevicko 2012年

7
@crazyrails:您能接受这个答案作为正确答案吗?这将帮助将来的搜索者找到针对您所描述问题的正确解决方案。谢谢!
奥利弗·

2
@ cha0site:是的,在XPath 2.0及更高版本中可以。该答案是在XPath 2.0正式发布之前编写的。请参阅stackoverflow.com/a/12165032/423105stackoverflow.com/a/12165195/423105
LarsH 2015年

1
不要像我一样,在本示例中删除正在寻找的班级周围的空格;它们实际上很重要。否则,它可能会起作用,但却无法达到目的。
CTS_AE


38

编辑:请参阅bobince的解决方案,该解决方案使用contains而不是start-with,以及确保在完整令牌级别进行比较的技巧(请注意,将“ atag”模式作为另一个“ tag”的一部分找到)。

“ atag btag”是class属性的一个奇数值,但更是如此,请尝试:

//*[starts-with(@class,"atag")]

如果您的XPath引擎支持starts-with命令,则可以使用此命令,例如,据我记得,JVM 6不支持它
Mohamed Faramawi 2009年

10
@mjv:CSS类属性通常会指定多个值。这就是CSS的完成方式。
skaffman

7
@mjv您不能保证该名称将出现在class属性的开头。
艾伦·克鲁格

@thuktun @skaffman。谢谢,很棒的评论。我被“重定向”到相应的绑定解决方案。
mjv

不适用于以上等价的<div class =“ btag atag”>
Alexei Vinogradov

30

一个有效的2.0 XPath:

//*[tokenize(@class,'\s+')='atag']

或带有变量:

//*[tokenize(@class,'\s+')=$classname]

如果@class有多个元素,怎么办?因为它将返回单词列表,并将其与字符串进行比较失败,并且基数错误
2014年

3
@AlexisWilke-从规范(w3.org/TR/xpath20/#id-general-comparisons):常规比较是存在量化的比较,可以应用于任何长度的操作数序列。我尝试过的每个2.0处理器都可以使用它。
丹尼尔·哈利

1
另请注意,在XPath 3.1中,可以将其简化为//*[tokenize(@class)=$classname]
Michael Kay,

1
为了完整起见,如果您很幸运地使用了可识别架构的XPath处理器,并且@class具有列表值类型,那么您可以简单地编写//*[@class=$classname]
Michael Kay

21

请注意,如果您可以假设感兴趣的类名不是另一个可能的类名的子字符串,bobince的答案可能会过于复杂。如果是这样,则可以通过contains函数简单地使用子字符串匹配。以下内容将匹配其类包含子字符串'atag'的任何元素:

//*[contains(@class,'atag')]

如果上述假设不成立,则子字符串匹配将匹配您不想要的元素。在这种情况下,您必须找到边界字。通过使用空格定界符来查找类名称边界,bobince的第二个答案将找到确切的匹配项:

//*[contains(concat(' ', normalize-space(@class), ' '), ' atag ')]

这将匹配atag而不匹配matag


这是我一直在寻找的解决方案。它显然在class ='hello test-world'中找到'test',但与'hello test-test world'不匹配。由于我仅使用XPath 1.0,并且没有RegEx,因此这只是可行的解决方案。
Jan Stanicek '16

7

添加到bobince的答案...如果您使用的任何工具/库都使用Xpath 2.0,您也可以执行以下操作:

//*[count(index-of(tokenize(@class, '\s+' ), $classname)) = 1]

显然需要count(),因为index-of()返回字符串中与之匹配的每个索引的序列。


1
我想您是要不要将$classname变量放在引号之间?因为它就是一个字符串。
亚历克西斯·威尔克

1
最后,除了字符串文字外'$classname',还有一个正确的(兼容JavasScript的)getElementsByClassName ...实现。
Joel Mellon

1
这非常复杂。有关正确的XPath 2.0答案,请参见@DanielHaley的响应。
Michael Kay


0

我来这里是为了寻找Ranorex Studio 9.0.1的解决方案。尚无contains()。相反,我们可以使用如下正则表达式:

div[@class~'atag']

-1

对于包含公共URL的链接,必须在变量中进行控制台。然后依次尝试。

webelements allLinks=driver.findelements(By.xpath("//a[contains(@href,'http://122.11.38.214/dl/appdl/application/apk')]"));
int linkCount=allLinks.length();
for(int i=0; <linkCount;i++)
{
    driver.findelement(allLinks[i]).click();
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.