Seleccionar una clase css con xpath

87

Quiero seleccionar solo una clase llamada .date

Por alguna razón, no puedo hacer que esto funcione. Si alguien sabe qué está mal con mi código, se lo agradecería mucho.

@$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." ";
}
Teddy13
fuente
2
y ¿qué pasa con el fragmento de HTML? (Prefiera mostrarnos la salida simpleXml de asXML () ya que está más cerca de xpath)
SergeS
si hay varias clases que necesita hacercontains(@class, 'date')
Gordon
La respuesta de @ Gordon es peligrosa, si el atributo de clase es "datetime", también coincidiría. La respuesta de user716736 es más completa.
Niels Bom

Respuestas:

242

Quiero escribir la respuesta canónica a esta pregunta porque la respuesta anterior tiene un problema.

Nuestro problema

El selector de CSS :

.foo

seleccionará cualquier elemento que tenga la clase foo .

¿Cómo se hace esto en XPath?

Aunque XPath es más poderoso que CSS, XPath no tiene un equivalente nativo de un selector de clases CSS . Sin embargo, existe una solución.

La forma correcta de hacerlo

El selector equivalente en XPath es:

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

La función normalizar el espacio elimina los espacios en blanco iniciales y finales (y también reemplaza las secuencias de caracteres de espacios en blanco por un solo espacio).

(En un sentido más general) también es el equivalente del selector CSS:

*[class~="foo"]

que coincidirá con cualquier elemento cuyo valor de atributo de clase sea ​​una lista de valores separados por espacios en blanco, uno de los cuales es exactamente igual a foo .

Un par de formas obvias pero incorrectas de hacerlo

El selector XPath:

//*[@class="foo"]

no funciona! porque no coincidirá con un elemento que tenga más de una clase, por ejemplo

<div class="foo bar">

Tampoco coincidirá si hay espacios en blanco adicionales alrededor del nombre de la clase:

<div class="  foo ">

El selector XPath 'mejorado'

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

¡tampoco funciona! porque coincide incorrectamente con elementos de la clase foobar , por ejemplo

<div class="foobar">

El crédito es para este tipo, que fue la primera solución publicada para este problema que encontré en la web: http://dubinko.info/blog/2007/10/01/simple-parsing-of-space-seprated-attributes- in-xpathxslt /

usuario716736
fuente
¿Cuál es la necesidad de normalizar el espacio?
Freek
"la respuesta anterior" probablemente se refiere a la de MrGlass.
LarsH
¿Es esto posible <div class="foo\tbar">? Quiero decir, nombres de clases separados por una pestaña.
Frozen Flame
1
pero <div class = "group-conditions" /> y <div class = "condition" /> es lo mismo para $ x ('// div [contains (concat ("", normalize-space (@class), " ")," condition ")] ')
Memke
1
@ testerjoe2 ¿lo intentaste //*[contains(concat(" ", normalize-space(@class), " "), " foo ")]?
Niels Bom
11

//[@class="date"] no es un xpath válido.

Prueba //*[@class="date"], o si sabes que es una imagen,//img[@class="date"]

MrGlass
fuente
7

XPath 3.1 introduce una función contiene-token y, por tanto, finalmente resuelve esto 'oficialmente'. Está diseñado para apoyar clases .

Ejemplo:

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

Esta función asegura que el espacio en blanco (no solo (U + 0020)) se maneja correctamente, funciona en caso de repetición del nombre de clase y generalmente cubre los casos extremos.


Nota: A partir de hoy (13/12/2016) XPath 3.1 tiene el estado de recomendación candidata .

Robin Pokorny
fuente
No funciona en el último Chrome de hoy. Hasta que funcione, ¿cómo podemos evitar la limitación de que // * [contiene (@class, "foo")] también seleccionará cualquier clase que contenga foo, como foobar, fooz, etc.
MasterJoe
1

HTML permite nombres de atributos y elementos que no distinguen entre mayúsculas y minúsculas y luego la clase es una lista de nombres de clase separada por espacios. Aquí vamos por una imgetiqueta y el classnombre date:

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

Ver también: Conversión de CSS Selector a XPath

hakre
fuente
1

¡¡¡CUIDADO CON LOS SIGNOS MENOS EN LA PLANTILLA !!! Si está consultando "my-ownclass" en DOM:

<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.
Vlado
fuente