Ayant un balisage comme celui-ci:
<div class="foo">
<div><span class="a1"></span><a href="...">...</a></div>
<div><span class="a2"></span><a href="...">...</a></div>
<div><span class="a1"></span>some text</div>
<div><span class="a3"></span>some text</div>
</div>
Je souhaite obtenir tous les <a>
Et some text
[~ # ~] seulement [~ # ~] s'ils sont adjacents span
est de classe a1
. Donc, à la fin de tout le code, mon résultat devrait être <a>
Du premier div
et some text
Du troisième. Ce serait facile si <a>
Et some text
Étaient à l'intérieur de span
ou div
auraient l'attribut class
, mais pas de chance.
Ce que je fais maintenant, c'est rechercher span
avec la classe a1
:
//div[contains(@class,'foo')]/div/span[contains(@class,'a1')]
puis j'obtiens son parent et fais une autre query()
avec ce parent comme nœud de contexte. Cela semble tout simplement loin d'être efficace, la question est donc clairement de savoir s'il existe un meilleur moyen d'atteindre mon objectif.
L'ADDENDUM À LA RÉPONSE
Selon @MarcB réponse acceptée , la bonne requête à utiliser est:
//div[contains(@class,'foo')]/div/span[contains(@class,'a1')]/..
mais pour <a>
il peut être préférable d'utiliser:
//div[contains(@class,'foo')]/div/span[contains(@class,'a1')]/../a
récupérez le <a>
au lieu de son conteneur.
La bonne chose à propos des requêtes xpath est que vous pouvez essentiellement les traiter comme un chemin d'accès au système de fichiers, donc simplement avoir
//div[contains(@class,'foo')]/div/span[contains(@class,'a1')]/..
^^
trouvera tous vos nœuds .a1 qui sont en dessous d'un nœud .foo, puis montera d'un niveau vers les parents des nœuds a1.
ne expression qui est meilleure qu'en utilisant l'axe inverse:
//div[contains(@class,'foo')]/div[span[contains(@class,'a1')]]
Cela sélectionne tout div
qui est un enfant d'un div
dont l'attribut class
contient la chaîne "foo" et qui (le div
sélectionné) a un span
enfant dont l'attribut class
contient la chaîne "a1".
vérification basée sur XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:copy-of select=
"//div[contains(@class,'foo')]
/div[span[contains(@class,'a1')]]"/>
</xsl:template>
</xsl:stylesheet>
Lorsque cette transformation est appliquée sur le document XML fourni:
<div class="foo">
<div><span class="a1"></span><a href="...">...</a></div>
<div><span class="a2"></span><a href="...">...</a></div>
<div><span class="a1"></span>some text</div>
<div><span class="a3"></span>some text</div>
</div>
l'expression XPath est évaluée et les éléments sélectionnés sont copiés dans la sortie:
<div>
<span class="a1"/>
<a href="...">...</a>
</div>
<div>
<span class="a1"/>some text</div>
II. Remarques sur l'accès à un élément Html par l'une de ses classes:
S'il est connu que l'élément ne peut avoir qu'une seule classe, alors il n'est pas du tout nécessaire d'utiliser contains()
Ne pas utiliser:
//div[contains(@class, 'foo')]
tiliser:
//div[@class = 'foo']
ou, s'il peut y avoir des espaces de début/fin, utilisez:
//div[normalize-space(@class) = 'foo']
n problème crucial avec:
//div[contains(@class, 'foo')]
est que cela sélectionne tout div
avec une classe telle que "myfoo", "foo2" ou "myfoo3".
Si l'élément peut avoir plusieurs classes et pour éviter le problème ci-dessus, l'expression XPath correcte est:
//div[contains(concat(' ', @class, ' '), ' foo ')]