459 votes

Comment faire correspondre un attribut qui contient une certaine chaîne ?

J'ai un problème pour sélectionner les nœuds par attribut lorsque l'attribut contient plus d'un mot. Par exemple :

<div class="atag btag" />

C'est mon expression xpath :

//*[@class='atag']

L'expression fonctionne avec

<div class="atag" />

mais pas pour l'exemple précédent. Comment puis-je sélectionner le <div> ?

9 votes

Il est utile de préciser, je pense, que "atag btag" est un seul attribut, et non deux. Vous essayez de faire une correspondance de sous-chaîne dans xpath.

3 votes

Oui, vous avez raison, c'est ce que je veux.

0 votes

513voto

surupa123 Points 1525

Voici un exemple qui trouve les éléments div dont le className contient atag :

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

Voici un exemple qui trouve les éléments div dont le className contient atag et btag :

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

Cependant, il trouvera également des correspondances partielles comme class="catag bobtag" .

Si vous ne voulez pas de correspondances partielles, voir la réponse de Bobince ci-dessous.

125 votes

@Redbeard : C'est une réponse littérale mais ce n'est généralement pas ce que devrait viser une solution de correspondance de classe. En particulier, cela correspondrait à <div class="Patagonia Halbtagsarbeit"> qui contient les chaînes cibles mais n'est pas un div avec les classes données.

3 votes

Cela fonctionnera pour des scénarios simples, mais faites attention si vous voulez utiliser cette réponse dans des contextes plus larges avec moins ou pas de contrôle sur les valeurs d'attributs que vous vérifiez. La bonne réponse est bobince's.

16 votes

Désolé, cela ne correspond pas à une classe, mais à une sous-chaîne.

309voto

bobince Points 270740

La réponse de mjv est un bon début mais échouera si atag n'est pas le premier nom de classe listé.

L'approche habituelle est plutôt lourde :

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

cela fonctionne tant que les classes sont séparées par des espaces uniquement, et non par d'autres formes d'espaces blancs. C'est presque toujours le cas. Si ce n'est pas le cas, vous devez rendre le système encore plus difficile à manier :

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

(La sélection par des chaînes de caractères séparées par des espaces, comme les noms de classe, est un cas tellement courant qu'il est surprenant qu'il n'existe pas de fonction XPath spécifique pour cela, comme la fonction '[class~="atag"] de CSS3).

60 votes

Bah, xpath a besoin de quelques corrections

0 votes

La réponse de surupa123 ci-dessous est bien meilleure, elle utilise contains() juste avec la classe qui vous intéresse, pas besoin de concats d'après ce que je vois.

0 votes

Je ne pouvais pas. tokenize , exists et index-of peuvent être combinées pour résoudre ce problème d'une manière plutôt moche mais très concise ?

41voto

SelenUser Points 12

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

2 votes

Et si le nom de la classe est grabatagonabag ? (Indice : ça correspondra toujours.)

39voto

mjv Points 38081

EDIT : voir la solution de Bobince qui utilise contient plutôt que commencez avec ainsi qu'une astuce pour s'assurer que la comparaison est effectuée au niveau d'un token complet (de peur que le motif 'atag' ne soit trouvé dans une autre 'tag').

"atag btag" est une valeur étrange pour l'attribut class, mais essayez quand même :

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

0 votes

Vous pouvez l'utiliser si votre moteur XPath supporte la commande starts-with, par exemple la JVM 6 ne la supporte pas autant que je m'en souvienne

10 votes

@mjv : Il est courant qu'un attribut de classe CSS spécifie plusieurs valeurs. C'est comme ça que CSS est fait.

7 votes

@mjv Vous ne pouvez pas garantir que ce nom apparaîtra au début de l'attribut de classe.

30voto

Daniel Haley Points 19098

Un XPath 2.0 qui fonctionne :

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

ou avec une variable :

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

0 votes

Comment cela peut-il fonctionner si @class a plus d'un élément ? Parce qu'il va retourner une liste de mots et que la comparaison avec une chaîne de caractères échoue avec cardinalité erronée .

3 votes

@AlexisWilke - De la spécification ( w3.org/TR/xpath20/#id-comparaisons-générales ) : Les comparaisons générales sont des comparaisons existentiellement quantifiées qui peuvent être appliquées à des séquences d'opérandes de toute longueur. Ça a marché dans tous les processeurs 2.0 que j'ai essayés.

1 votes

Notez également que dans XPath 3.1, cela peut être simplifié en //*[tokenize(@class)=$classname]

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X