使用XPath选择CSS类

91
我想要选择一个叫做.date的类。
不知道为什么,我无法让它起作用。如果有人知道我的代码有什么问题,那将不胜感激。
@$doc = new DOMDocument();
@$doc->loadHTML($html);
$xml = simplexml_import_dom($doc); // just to make xpath more simple
$images = $xml->xpath('//[@class="date"]');                             
foreach ($images as $img)
{
    echo  $img." ";
}

2
那HTML的部分呢?(最好展示一下simpleXml输出的asXML(),因为它更接近xpath) - SergeS
如果有多个类,您需要使用contains(@class, 'date') - Gordon
重复:当然。 危险:我将重新表述为:您可能会得到比您预期的更多。 很抱歉,但我认为您的评论(“如果有...'date'”)不够清楚。 - Niels Bom
相关:https://dev59.com/p3I-5IYBdhLWcg3w8NSB和https://dev59.com/gnM_5IYBdhLWcg3waSX9 - Timo Huovinen
显示剩余4条评论
6个回答

257

我希望撰写这个问题的通用答案,因为上面的答案有一个问题。

我们的问题

CSS选择器:

.foo

这将选择具有类foo的任何元素。

XPath该如何实现?

尽管XPath比CSS更强大,XPath没有本地等效的CSS类选择器。不过,有一种解决方案。

正确的方法

XPath中的等效选择器是:

//*[contains(concat(" ", normalize-space(@class), " "), " foo ")]

函数 normalize-space 可以去除字符串前后的空格(并将连续的空格字符替换为一个空格)。

在更广泛的意义上,这也相当于CSS选择器:

*[class~="foo"]

这将匹配任何一个元素,其class属性值为由空格分隔的值列表,其中一个值恰好等于foo

一些明显但错误的方法

XPath选择器:

//*[@class="foo"]

不起作用!因为它不能匹配具有多个类的元素,例如

<div class="foo bar">

它也不会匹配类名周围的任何额外空格:
<div class="  foo ">

“改良版”XPath选择器

//*[contains(@class, "foo")]

这也不起作用!因为它错误地匹配了具有类名foobar的元素,例如

<div class="foobar">

感谢这位先生,他是我在网络上找到的最早解决这个问题的发布者: http://dubinko.info/blog/2007/10/01/simple-parsing-of-space-seprated-attributes-in-xpathxslt/


1
normalize-space有什么必要性? - Freek
“the answer above” 可能指的是 MrGlass 的回答。 - LarsH
1
这种写法 <div class="foo\tbar"> 可以实现吗?我的意思是,类名之间用制表符分隔。 - Frozen Flame
1
但是 <div class="group-conditions"/> 和 <div class="condition"/> 对于 $x('//div[contains(concat(" ", normalize-space(@class), " "), "condition")]') 是相同的。 - Memke
1
@testerjoe2,你试过 //*[contains(concat(" ", normalize-space(@class), " "), " foo ")] 吗? - Niels Bom
显示剩余2条评论

13

//[@class="date"] 不是一个有效的 xpath。

请尝试使用 //*[@class="date"],或者如果您确定它是一个图像,请使用 //img[@class="date"]


7

XPath 3.1引入了一个名为contains-token的函数,从而正式解决了这个问题。它被设计用于支持类

例如:

//*[contains-token(@class, "foo")]

该函数确保正确处理空格(不仅限于 (U+0020)),适用于类名重复的情况,并通常涵盖边缘情况。


注意:截至今天(2016年12月13日),XPath 3.1已经成为了“候选推荐”状态。


它在今天最新的Chrome中无法工作。在它能够正常工作之前,我们该如何绕过这个限制呢?例如,//*[contains(@class, "foo")]将选择任何包含foo的类,比如foobar、fooz等。 - MasterJoe

3

很不幸,截至2017年6月12日,Chrome似乎还没有实现这个功能。根据https://en.wikipedia.org/wiki/Comparison_of_layout_engines_(XML)#Query_technologies上的信息,它在各方面都缺乏。 - JonnyRaa

1

注意模板中的负号!如果你在 DOM 中查询“my-ownclass”:

<ul class="my-ownclass"><li>...</li></ul>
<ul class="someother"><li>...</li></ul>
<ul><li>...</li></ul>

$finder = new DomXPath($dom);
$nodes = $finder->query(".//ul[contains(@class, 'my-ownclass')]"); // This will NOT behave as expected! This will strangely match all the <ul> elements in DOM.
$nodes = $finder->query(".//ul[contains(@class, 'ownclass')]"); // This will match the element.

1

HTML允许不区分大小写的元素和属性名称,而类是一个以空格分隔的类名列表。这里我们使用一个img标签和名为dateclass

//*['IMG' = translate(name(.), 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')]/@*['CLASS' = translate(name(.), 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') and contains(concat(' ', normalize-space(.), ' '), concat(' ', 'date', ' '))]

另请参阅:CSS选择器转XPath


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接