Come posso abbinare un attributo che contiene una determinata stringa?


442

Sto riscontrando un problema nella selezione dei nodi per attributo quando gli attributi contengono più di una parola. Per esempio:

<div class="atag btag" />

Questa è la mia espressione xpath:

//*[@class='atag']

L'espressione funziona con

<div class="atag" />

ma non per l'esempio precedente. Come posso selezionare <div>?


9
Vale la pena sottolineare, penso, che "atag btag" è un singolo attributo, non due. Stai cercando di eseguire la corrispondenza della sottostringa in xpath.
Skaffman,

3
Sì, hai ragione, questo è quello che voglio.
crazyrails,


1
Ecco perché dovresti usare un selettore CSS ... div.atago div.btag. Super semplice, non corrispondenza delle stringhe e MODO più veloce (e meglio supportato nei browser). XPath (contro HTML) dovrebbe essere relegato a ciò che è utile per ... trovare elementi con testo contenuto e per la navigazione DOM.
JeffC,

Risposte:


486

Ecco un esempio che trova elementi div il cui className contiene atag:

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

Ecco un esempio che trova elementi div il cui className contiene atage btag:

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

Tuttavia, troverà anche corrispondenze parziali come class="catag bobtag".

Se non desideri corrispondenze parziali, vedi la risposta di Bobince di seguito.


123
@Redbeard: è una risposta letterale, ma di solito non dovrebbe mirare a una soluzione di abbinamento di classe. In particolare corrisponderebbe <div class="Patagonia Halbtagsarbeit">, che contiene le stringhe target ma non è un div con le classi date.
bobince

3
Funzionerà per scenari semplici, ma fai attenzione se vuoi usare questa risposta in contesti più ampi con meno o nessun controllo sui valori degli attributi che stai verificando. La risposta corretta è di Bobince.
Oliver,

16
Siamo spiacenti, questo non corrisponde a una classe, corrisponde a una sottostringa
Timo Huovinen

5
è chiaramente sbagliato perché trova anche: <div class = "annatag bobtag"> che non dovrebbe.
Alexei Vinogradov,

6
La domanda era "contiene una determinata stringa" e non "corrisponde a una determinata classe"
Alsaziano

303

La risposta di mjv è un buon inizio, ma fallirà se atag non è il primo nome di classe elencato.

L'approccio abituale è piuttosto ingombrante:

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

funziona fintanto che le classi sono separate solo da spazi e non da altre forme di spazi bianchi. Questo è quasi sempre il caso. Se potrebbe non essere, devi renderlo ancora più ingombrante:

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

(La selezione per stringhe separate da spazi di tipo classname è un caso così comune è sorprendente che non ci sia una funzione XPath specifica per essa, come '[class ~ = "atag"] di CSS3.)


58
bah, xpath ha bisogno di alcune correzioni
Randy L

13
@Redbeard La risposta di supra123 è problematica se esiste una classe css come "atagnumbertwo" che non vuoi selezionare, anche se ammetto che potrebbe non essere probabile (:
drevicko,

7
@crazyrails: potresti per favore accettare questa risposta come la risposta corretta? Ciò aiuterà i futuri ricercatori a identificare la soluzione corretta al problema descritto dalla tua domanda. Grazie!
Oliver,

2
@ cha0site: Sì, in XPath 2.0 e seguenti. Questa risposta è stata scritta prima che XPath 2.0 diventasse ufficiale. Vedere stackoverflow.com/a/12165032/423105 o stackoverflow.com/a/12165195/423105
Larsh

1
Non essere come me e rimuovere gli spazi intorno alla classe che stai cercando in questo esempio; sono in realtà importanti. Altrimenti potrebbe sembrare funzionante ma vanifica lo scopo.
CTS_AE,

40

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


Cosa succede se il nome della classe è grabatagonabag? (Suggerimento: continuerà a corrispondere.)
Wayne, il

38

EDIT : vedi la soluzione di bobince che usa contiene piuttosto che iniziare , insieme a un trucco per garantire che il confronto sia fatto a livello di un token completo (per evitare che il modello 'atag' sia trovato come parte di un altro 'tag').

"atag btag" è un valore dispari per l'attributo class, ma mai meno, prova:

//*[starts-with(@class,"atag")]

puoi usarlo se il tuo motore XPath supporta il comando start-with, ad esempio JVM 6 non lo supporta per quanto ricordo
Mohamed Faramawi,

10
@mjv: è comune per un attributo di classe CSS specificare più valori. Ecco come viene fatto CSS.
Skaffman,

7
@mjv Non puoi garantire che quel nome appaia all'inizio dell'attributo class.
Alan Krueger,

@thuktun @skaffman. Grazie, ottimi commenti. Ho "reindirizzato" alla soluzione di bobince di conseguenza.
mjv,

Non funziona per <div class = "btag atag"> che equivale a sopra
Alexei Vinogradov,

30

Un XPath 2.0 che funziona:

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

o con una variabile:

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

Come può funzionare se @classha più di un elemento? Perché restituirà un elenco di parole e il confronto con una stringa fallisce con una cardinalità errata .
Alexis Wilke,

3
@AlexisWilke - Dalla specifica ( w3.org/TR/xpath20/#id-general-comparisons ): i confronti generali sono confronti quantificati esistenzialmente che possono essere applicati a sequenze di operandi di qualsiasi lunghezza. Ha funzionato in ogni processore 2.0 che ho provato.
Daniel Haley,

1
Nota anche che in XPath 3.1 questo può essere semplificato//*[tokenize(@class)=$classname]
Michael Kay,

1
E per completezza, se sei abbastanza fortunato da utilizzare un processore XPath compatibile con lo schema e se @class ha un tipo con valori di elenco, puoi semplicemente scrivere//*[@class=$classname]
Michael Kay,

21

Tieni presente che la risposta di Bobince potrebbe essere eccessivamente complicata se puoi presumere che il nome della classe che ti interessa non sia una sottostringa di un altro nome di classe possibile . Se questo è vero, puoi semplicemente usare la corrispondenza della sottostringa tramite la funzione contiene. Quanto segue corrisponderà a qualsiasi elemento la cui classe contiene la sottostringa 'atag':

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

Se il presupposto di cui sopra non regge, una corrispondenza di sottostringa corrisponderà a elementi che non intendi. In questo caso, devi trovare i confini della parola. Usando i delimitatori di spazio per trovare i confini del nome della classe, la seconda risposta di Bobince trova le corrispondenze esatte:

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

Questo corrisponderà atage non matag.


Questa è la soluzione che stavo cercando. Trova chiaramente "test" in class = "hello test world" e non corrisponde a "hello test-test world". Poiché utilizzo solo XPath 1.0 e non ho RegEx, questa è l'unica soluzione che funziona.
Jan Stanicek,

7

Per aggiungere la risposta di bobince ... Se qualunque strumento / libreria tu usi usi Xpath 2.0, puoi anche fare questo:

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

count () è apparentemente necessario perché index-of () restituisce una sequenza di ogni indice con cui ha una corrispondenza nella stringa.


1
Suppongo che intendevi NON inserire la $classnamevariabile tra virgolette? Perché così com'è, questa è una stringa.
Alexis Wilke,

1
Infine, un'implementazione corretta (compatibile con JavasScript) di getElementsByClassName ... a parte la stringa, '$classname'ovviamente.
Joel Mellon,

1
Questo è molto complicato. Vedi la risposta di @ DanielHaley per la corretta risposta XPath 2.0.
Michael Kay,


0

Sono venuto qui alla ricerca di una soluzione per Ranorex Studio 9.0.1. Non ci sono ancora contiene (). Invece possiamo usare regex come:

div[@class~'atag']

-1

Per i collegamenti che contengono l'URL comune è necessario console in una variabile. Quindi provare in sequenza.

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