Utilisation de XPath pour sélectionner des attributs avec des caractères génériques

c# html html-agility-pack xpath

Question

J'ai besoin d'analyser le code HTML et j'utilise C # et Html Agility Pack Library pour effectuer la sélection des nœuds. Mon html ressemblera à quelque chose comme:

<input data-translate-atrr-placeholder="FORGOT_PASSWORD.FORM.EMAIL">

ou :

<h1 data-translate="FORGOT_PASSWORD.FORM.EMAIL"></h1>

data-translate-attr-**** est le nouveau modèle d'attributs que je dois trouver

Je pourrais utiliser quelque chose comme ça:

//[contains(@??,'data-translate-attr')]

mais malheureusement, cela ne cherchera que la valeur DANS un attribut. Comment rechercher l'attribut lui-même, avec un caractère générique?

Mise à jour: @Mathias Muller

HtmlAgilityPack.HtmlDocument htmlDoc    
// this is the old code (returns nodes)
var nodes = htmlDoc.DocumentNode.SelectNodes("//@data-translate");  
// these suggestions return no nodes using the same data
var nodes = htmlDoc.DocumentNode.SelectNodes("//@*[contains(name(),'data-translate')]");  
var nodes = htmlDoc.DocumentNode.SelectNodes("//@*[starts-with(name(),'data-translate')]");

Mise à jour 2

Cela semble être un problème de Pack d'agilité HTML plus qu'un problème de XPath, j'ai utilisé chrome pour tester mes expressions XPath et toutes les solutions suivantes ont fonctionné en chrome mais pas en Pack d'agilité HTML:

//@*[contains(local-name(),'data-translate')]
//@*[starts-with(name(),'data-translate')]
//attribute::*[starts-with(local-name(.),'data-translate')]

Ma solution

J'ai fini par faire les choses à l'ancienne ...

var nodes = htmlDoc.DocumentNode.SelectNodes("//@*");

if (nodes != null) {
    foreach (HtmlNode node in nodes) {
        if (node.HasAttributes) {
            foreach (HtmlAttribute attr in node.Attributes) {
                if (attr.Name.StartsWith("data-translate")) {
                    // code in here to handle translation node
                }
            }
        }
    }
}

Réponse acceptée

Utilisez les fonctions XPath contains() ou starts-with() . Vous avez besoin d'une expression XPath telle que

//@*[contains(name(),'data-translate')]

ou peut-être

//@*[starts-with(name(),'data-translate')]

qui récupère réellement les nœuds d' attribut . Ci-dessus, le @* est l'attribut générique que vous recherchiez.


Réponse populaire

Plutôt que d'utiliser name() , utilisez local-name() tel que:

var nodes = htmlDoc.DocumentNode.SelectNodes("//@*[starts-with(local-name(),'data-translate')]");

la différence est que name() devrait vous donner le nom de l'attribut avec un préfixe tel qu'un espace de nom en xml, et local-name() émettra ce préfixe s'il est là, dans votre name() cas name() et local-name() devrait fonctionne de la même manière car son code HTML et il n’existe pas d’espaces de noms, mais il semblerait qu’ils ne le fassent pas et c’est probablement un bogue.

Tester:

    var html = "<h3 x='foo'></h3>";
    var doc = new HtmlAgilityPack.HtmlDocument();
    doc.LoadHtml(html);
    var ElementByName = doc.DocumentNode.SelectSingleNode("//*[name()='h3']");                //Works
    var ElementByLocalName = doc.DocumentNode.SelectSingleNode("//*[local-name()='h3']");     //Works
    var ElementByAttributeLocalName = doc.DocumentNode.SelectSingleNode("//*[@*[local-name()='x']]"); //Works
    var ElementByAttributeName = doc.DocumentNode.SelectSingleNode("//*[@*[name()='x']]");  //Does NOT

    //Mathias Way
    var ElementByAttributeLocalName_ = doc.DocumentNode.SelectSingleNode("//@*[local-name() = 'x']"); //Works
    var ElementByAttributeName_ = doc.DocumentNode.SelectSingleNode("//@*[name() = 'x']");  //Does NOT


Related

Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow