Auswählen einer CSS-Klasse mit xpath

87

Ich möchte nur eine eigene Klasse namens .date auswählen

Aus irgendeinem Grund kann ich das nicht zum Laufen bringen. Wenn jemand weiß, was mit meinem Code nicht stimmt, wäre er sehr dankbar.

@$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
quelle
2
und was ist mit einem Stück HTML? (Zeigen Sie uns lieber eine einfache XML-Ausgabe von asXML (), da diese näher an xpath liegt.)
SergeS
Wenn es mehrere Klassen gibt, müssen Sie tuncontains(@class, 'date')
Gordon
@ Gordons Antwort ist gefährlich, wenn das Klassenattribut "datetime" ist, würde es auch übereinstimmen. Die Antwort von user716736 ist vollständiger.
Niels Bom

Antworten:

242

Ich möchte die kanonische Antwort auf diese Frage schreiben, da die obige Antwort ein Problem hat.

Unser Problem

Der CSS- Selektor:

.foo

wählt jedes Element aus, das die Klasse foo hat .

Wie machst du das in XPath?

Obwohl XPath leistungsfähiger als CSS ist, verfügt XPath nicht über ein natives Äquivalent zu einem CSS-Klassenselektor . Es gibt jedoch eine Lösung.

Der richtige Weg, es zu tun

Der entsprechende Selektor in XPath lautet:

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

Die Funktion " Raum normalisieren" entfernt führende und nachfolgende Leerzeichen (und ersetzt auch Sequenzen von Leerzeichen durch ein einzelnes Leerzeichen).

(Im allgemeineren Sinne) Dies ist auch das Äquivalent des CSS-Selektors:

*[class~="foo"]

Dies entspricht jedem Element, dessen Klassenattributwert eine Liste von durch Leerzeichen getrennten Werten ist, von denen einer genau gleich foo ist .

Ein paar offensichtliche, aber falsche Wege, dies zu tun

Der XPath-Selektor:

//*[@class="foo"]

funktioniert nicht! weil es beispielsweise nicht mit einem Element übereinstimmt, das mehr als eine Klasse hat

<div class="foo bar">

Es stimmt auch nicht überein, wenn der Klassenname ein zusätzliches Leerzeichen enthält:

<div class="  foo ">

Der 'verbesserte' XPath-Selektor

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

funktioniert auch nicht! weil es zum Beispiel Elemente falsch mit der Klasse foobar vergleicht

<div class="foobar">

Dank geht an diesen Kerl, der die früheste veröffentlichte Lösung für dieses Problem war, die ich im Internet gefunden habe: http://dubinko.info/blog/2007/10/01/simple-parsing-of-space-seprated-attributes- in-xpathxslt /

user716736
quelle
Was ist der Bedarf an Normalisierungsraum?
Freek
"die Antwort oben" bezieht sich wahrscheinlich auf MrGlass.
LarsH
Ist das möglich <div class="foo\tbar">? Ich meine, Klassennamen durch Tabulatoren getrennt.
Frozen Flame
1
aber <div class = "Gruppenbedingungen" /> und <div class = "Bedingung" /> sind für $ x gleich ('// div [enthält (concat ("", normalize-space (@class), ") ")," Bedingung ")] ')
Memke
1
@ testerjoe2 hast du es versucht //*[contains(concat(" ", normalize-space(@class), " "), " foo ")]?
Niels Bom
11

//[@class="date"] ist kein gültiger xpath.

Versuchen //*[@class="date"]Sie es, oder wenn Sie wissen, dass es sich um ein Bild handelt,//img[@class="date"]

MrGlass
quelle
7

XPath 3.1 führt eine Funktion ein, die ein Token enthält, und löst dieses Problem schließlich "offiziell". Es wurde entwickelt, um Klassen zu unterstützen .

Beispiel:

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

Diese Funktion stellt sicher, dass Leerzeichen (nicht nur (U + 0020)) korrekt behandelt werden, bei Wiederholung von Klassennamen funktionieren und im Allgemeinen die Randfälle abdecken.


Hinweis: Ab heute (13.12.2016) hat XPath 3.1 den Status der Kandidatenempfehlung .

Robin Pokorny
quelle
Es funktioniert nicht in der heutigen neuesten Chrom. Wie können wir die Einschränkung umgehen, dass // * [enthält (@class, "foo")] auch jede Klasse auswählt, die foo enthält, wie foobar, fooz usw.
MasterJoe
1

HTML erlaubt Element- und Attributnamen, bei denen die Groß- und Kleinschreibung nicht berücksichtigt wird, und dann ist class eine durch Leerzeichen getrennte Liste von Klassennamen. Hier gehen wir für ein imgTag und den classNamen date:

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

Siehe auch: Konvertierung von CSS Selector in XPath

hakre
quelle
1

ACHTUNG VON MINUSZEICHEN IN DER VORLAGE !!! Wenn Sie in DOM nach "my-ownclass" fragen:

<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
quelle