Selezione di una classe CSS con xpath


87

Voglio selezionare solo una classe chiamata .date

Per qualche motivo, non riesco a farlo funzionare. Se qualcuno sa cosa c'è che non va nel mio codice, sarebbe molto apprezzato.

@$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
e per quanto riguarda il pezzo di html? (Preferisco mostrarci un output XML semplice da asXML () poiché è più vicino a xpath)
SergeS

se ci sono più classi che devi farecontains(@class, 'date')
Gordon



La risposta di @ Gordon è pericolosa, se l'attributo class è "datetime" corrisponderebbe anche. La risposta di user716736 è più completa.
Niels Bom

Risposte:


242

Voglio scrivere la risposta canonica a questa domanda perché la risposta sopra ha un problema.

Il nostro problema

Il selettore CSS :

.foo

selezionerà qualsiasi elemento che abbia la classe foo .

Come si esegue questa operazione in XPath?

Sebbene XPath sia più potente del CSS, XPath non ha un equivalente nativo di un selettore di classi CSS . Comunque, c'è una soluzione.

Il modo giusto per farlo

Il selettore equivalente in XPath è:

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

La funzione normalize-space elimina gli spazi bianchi iniziali e finali (e sostituisce anche le sequenze di caratteri di spazi bianchi con un singolo spazio).

(In un senso più generale) questo è anche l'equivalente del selettore CSS:

*[class~="foo"]

che corrisponderà a qualsiasi elemento il cui valore dell'attributo di classe è un elenco di valori separati da spazi, uno dei quali è esattamente uguale a foo .

Un paio di modi ovvi, ma sbagliati per farlo

Il selettore XPath:

//*[@class="foo"]

non funziona! perché non corrisponderà a un elemento che ha più di una classe, ad esempio

<div class="foo bar">

Inoltre, non corrisponderà se sono presenti spazi bianchi aggiuntivi attorno al nome della classe:

<div class="  foo ">

Il selettore XPath "migliorato"

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

non funziona neanche! perché abbina erroneamente elementi con la classe foobar , ad esempio

<div class="foobar">

Il merito va a questo tizio, che è stata la prima soluzione pubblicata a questo problema che ho trovato sul web: http://dubinko.info/blog/2007/10/01/simple-parsing-of-space-seprated-attributes- in-xpathxslt /


Qual è la necessità di normalizzare lo spazio?
Freek

"la risposta sopra" si riferisce probabilmente a MrGlass.
LarsH

È possibile <div class="foo\tbar">? Voglio dire, nomi di classi separati da una tabulazione.
Frozen Flame

1
ma <div class = "group-conditions" /> e <div class = "condition" /> è lo stesso per $ x ('// div [contiene (concat ("", normalize-space (@class), " ")," condition ")] ')
Memke

1
@ testerjoe2 hai provato //*[contains(concat(" ", normalize-space(@class), " "), " foo ")]?
Niels Bom

11

//[@class="date"] non è un xpath valido.

Prova //*[@class="date"], o se sai che è un'immagine,//img[@class="date"]


7

XPath 3.1 introduce una funzione contains-token e quindi risolve finalmente questo problema "ufficialmente". È progettato per supportare le classi .

Esempio:

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

Questa funzione assicura che lo spazio bianco (non solo (U + 0020)) sia gestito correttamente, funziona in caso di ripetizione del nome della classe e generalmente copre i casi limite.


Nota: ad oggi (2016-12-13) XPath 3.1 ha lo stato di Candidate Raccomandazione .


Non funziona nell'ultimo cromo di oggi. Finché non funziona, come aggirare la limitazione che // * [contiene (@class, "foo")] selezionerà anche qualsiasi classe che contiene foo, come foobar, fooz ecc.
MasterJoe


1

L'HTML consente nomi di elementi e attributi senza distinzione tra maiuscole e minuscole e quindi class è un elenco di nomi di classi separati da spazi. Qui andiamo per un imgtag e il classnome date:

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

Vedi anche: conversione da CSS Selector a XPath


1

ATTENZIONE AI SEGNI MENO NEL MODELLO !!! Se stai cercando "my-ownclass" in 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.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.