Wie kann ich mit einem Attribut übereinstimmen, das eine bestimmte Zeichenfolge enthält?

442

Ich habe ein Problem bei der Auswahl von Knoten nach Attributen, wenn die Attribute mehr als ein Wort enthalten. Zum Beispiel:

<div class="atag btag" />

Dies ist mein xpath-Ausdruck:

//*[@class='atag']

Der Ausdruck funktioniert mit

<div class="atag" />

aber nicht für das vorherige Beispiel. Wie kann ich das auswählen <div>?

verrückte Schienen
quelle
9
Ich denke, es ist erwähnenswert, dass "atag btag" ein einzelnes Attribut ist, nicht zwei. Sie versuchen, in xpath einen Teilstring-Abgleich durchzuführen.
Skaffman
3
Ja, du hast recht - das ist was ich will.
crazyrails
1
Aus diesem Grund sollten Sie einen CSS-Selektor verwenden ... div.atagoder div.btag. Super einfach, kein String-Matching und viel schneller (und besser in Browsern unterstützt). XPath (gegen HTML) sollte auf das verwiesen werden, was es nützlich ist, um ... Elemente durch enthaltenen Text zu finden und für die DOM-Navigation.
JeffC

Antworten:

486

Hier ist ein Beispiel, das div-Elemente findet, deren Klassenname enthält atag:

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

Hier ist ein Beispiel, das div-Elemente findet, deren Klassenname atagund enthält btag:

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

Es werden jedoch auch teilweise Übereinstimmungen wie gefunden class="catag bobtag".

Wenn Sie keine Teilübereinstimmungen wünschen, lesen Sie die Antwort von Bobince weiter unten.

surupa123
quelle
123
@ Redbeard: Es ist eine wörtliche Antwort, aber normalerweise nicht das, was eine Klassenanpassungslösung anstreben sollte. Insbesondere würde es übereinstimmen <div class="Patagonia Halbtagsarbeit">, das die Zielzeichenfolgen enthält, aber kein div mit den angegebenen Klassen ist.
Bobince
3
Dies funktioniert für einfache Szenarien. Achten Sie jedoch darauf, ob Sie diese Antwort in einem größeren Kontext verwenden möchten, ohne oder mit geringerer Kontrolle über die Attributwerte, nach denen Sie suchen. Die richtige Antwort ist Bobince.
Oliver
16
Sorry, das passt nicht zu einer Klasse, es passt zu einem Teilstring
Timo Huovinen
5
es ist eindeutig falsch, da es auch findet: <div class = "annatag bobtag"> was es nicht sollte.
Alexei Vinogradov
6
Die Frage war "enthält eine bestimmte Zeichenfolge" nicht "entspricht einer bestimmten Klasse"
Elsässer
303

Die Antwort von mjv ist ein guter Anfang, schlägt jedoch fehl, wenn atag nicht der erste aufgeführte Klassenname ist.

Der übliche Ansatz ist der eher unhandliche:

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

Dies funktioniert so lange, wie Klassen nur durch Leerzeichen und nicht durch andere Formen von Leerzeichen getrennt sind. Dies ist fast immer der Fall. Wenn dies nicht der Fall ist, müssen Sie es noch unhandlicher machen:

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

(Die Auswahl durch klassennamenähnliche, durch Leerzeichen getrennte Zeichenfolgen ist ein so häufiger Fall, dass es überraschend ist, dass es keine spezifische XPath-Funktion dafür gibt, wie z. B. CSS3s '[class ~ = "atag"]'.)

Bobince
quelle
57
bah, xpath braucht einige Korrekturen
Randy L
13
@ Redbeard supra123 Antwort ist problematisch, wenn es eine CSS-Klasse wie "atagnumbertwo" gibt, die Sie nicht auswählen möchten, obwohl ich zugeben werde, dass dies möglicherweise nicht wahrscheinlich ist (:
drevicko
7
@crazyrails: Könnten Sie bitte diese Antwort als die richtige Antwort akzeptieren? Dies hilft zukünftigen Suchenden, die richtige Lösung für das in Ihrer Frage beschriebene Problem zu finden. Vielen Dank!
Oliver
2
@ cha0site: Ja, sie könnten in XPath 2.0 und folgenden. Diese Antwort wurde geschrieben, bevor XPath 2.0 offiziell wurde. Siehe stackoverflow.com/a/12165032/423105 oder stackoverflow.com/a/12165195/423105
LarsH
1
Sei nicht wie ich und entferne die Leerzeichen um die Klasse, nach der du in diesem Beispiel suchst. Sie sind tatsächlich wichtig. Andernfalls scheint es zu funktionieren, macht aber den Zweck zunichte.
CTS_AE
41

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

SelenUser
quelle
38

EDIT : siehe bobince-Lösung , die Anwendungen enthalten , anstatt Anfahren mit , zusammen mit einem Trick des Vergleich zu gewährleisten , ist auf der Ebene eines vollständigen Token gemacht (damit die ‚atag‘ Muster als Teil eines anderen ‚Tages‘ zu finden).

"atag btag" ist ein ungerader Wert für das Klassenattribut, aber versuchen Sie trotzdem:

//*[starts-with(@class,"atag")]
mjv
quelle
Sie können dies verwenden, wenn Ihre XPath-Engine den Start-mit-Befehl unterstützt, zum Beispiel JVM 6 unterstützt ihn nicht, soweit ich mich erinnere
Mohamed Faramawi
10
@mjv: Es ist üblich, dass ein CSS-Klassenattribut mehrere Werte angibt. So wird CSS gemacht.
Skaffman
7
@mjv Sie können nicht garantieren, dass dieser Name am Anfang des Klassenattributs angezeigt wird.
Alan Krueger
@thuktun @skaffman. Danke, tolle Kommentare. Ich habe die Lösung entsprechend umgeleitet.
MJV
Funktioniert nicht für <div class = "btag atag">, was dem oben genannten entspricht
Alexei Vinogradov
30

Ein 2.0 XPath, der funktioniert:

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

oder mit einer Variablen:

//*[tokenize(@class,'\s+')=$classname]
Daniel Haley
quelle
Wie kann dies funktionieren, wenn @classmehr als ein Element vorhanden ist? Weil es eine Liste von Wörtern zurückgeben wird und der Vergleich mit einer Zeichenfolge mit falscher Kardinalität fehlschlägt .
Alexis Wilke
3
@AlexisWilke - Aus der Spezifikation ( w3.org/TR/xpath20/#id-general-comparisons ): Allgemeine Vergleiche sind existenziell quantifizierte Vergleiche, die auf Operandensequenzen beliebiger Länge angewendet werden können. Es funktioniert in jedem 2.0-Prozessor, den ich ausprobiert habe.
Daniel Haley
1
Beachten Sie auch, in XPath 3.1 kann dies vereinfacht werden//*[tokenize(@class)=$classname]
Michael Kay
1
Und der Vollständigkeit halber können Sie einfach schreiben//*[@class=$classname]
Michael Kay
21

Beachten Sie, dass die Antwort von Bobince möglicherweise zu kompliziert ist, wenn Sie davon ausgehen können, dass der Klassenname , an dem Sie interessiert sind, keine Teilzeichenfolge eines anderen möglichen Klassennamens ist . Wenn dies zutrifft, können Sie einfach den Teilstring-Abgleich über die Funktion enthält verwenden. Folgendes passt zu jedem Element, dessen Klasse den Teilstring 'atag' enthält:

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

Wenn die obige Annahme nicht zutrifft, stimmt eine Teilzeichenfolgenübereinstimmung mit Elementen überein, die Sie nicht beabsichtigen. In diesem Fall müssen Sie die Wortgrenzen finden. Durch die Verwendung der Leerzeichenbegrenzer zum Ermitteln der Klassennamengrenzen findet die zweite Antwort von Bobince die genauen Übereinstimmungen:

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

Dies wird übereinstimmen atagund nicht matag.

Brent Atkinson
quelle
Dies ist die Lösung, nach der ich gesucht habe. Es findet eindeutig "Test" in class = "Hallo Testwelt" und stimmt nicht mit "Hallo Test-Testwelt" überein. Da ich nur XPath 1.0 verwende und kein RegEx habe, ist dies nur eine Lösung, die funktioniert.
Jan Stanicek
7

So fügen Sie die Antwort von Bobince hinzu ... Wenn das von Ihnen verwendete Tool / die Bibliothek Xpath 2.0 verwendet, können Sie auch Folgendes tun:

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

count () wird anscheinend benötigt, da index-of () eine Sequenz jedes Index zurückgibt, bei dem eine Übereinstimmung in der Zeichenfolge vorliegt.

Armyofda12Mnkeys
quelle
1
Ich nehme an, Sie wollten die $classnameVariable NICHT zwischen Anführungszeichen setzen? Denn so wie es ist, ist das eine Zeichenfolge.
Alexis Wilke
1
Endlich eine korrekte (JavasScript-kompatible) Implementierung von getElementsByClassName ... abgesehen vom String-Literal '$classname'natürlich.
Joel Mellon
1
Dies ist grob überkompliziert. Die richtige XPath 2.0-Antwort finden Sie in der Antwort von @ DanielHaley.
Michael Kay
5

Sie können Folgendes versuchen

By.CssSelector("div.atag.btag")

Umesh Chhabra
quelle
0

Ich bin hierher gekommen, um nach einer Lösung für Ranorex Studio 9.0.1 zu suchen. Es gibt dort noch kein enthält (). Stattdessen können wir Regex verwenden wie:

div[@class~'atag']
Jarno Argillander
quelle
-1

Für die Links, die eine gemeinsame URL enthalten, muss eine Variable konsolidiert werden. Versuchen Sie es dann nacheinander.

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();
}
user3906232
quelle