Verwenden von XPath zum Auswählen von Attributen mit Platzhaltern

c# html html-agility-pack xpath

Frage

Ich habe HTML, das ich analysieren muss, und ich verwende C # und Html Agility Pack Library, um die Auswahl der Knoten zu treffen. Mein HTML wird ungefähr so ​​aussehen:

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

oder :

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

wo data-translate-attr-**** ist das neue Muster von Attributen, die ich finden muss

Ich könnte so etwas verwenden:

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

aber leider wird das nur nach einem INSIDE-Attribut gesucht. Wie suche ich nach dem Attribut selbst, mit einem Platzhalter?

Aktualisierung: @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')]");

Update 2

Dies scheint ein Html Agility Pack-Problem zu sein, mehr als ein XPath-Problem. Ich habe Chrome verwendet, um meine XPath-Ausdrücke zu testen, und alles Folgende funktionierte in Chrome, aber nicht in Html Agility Pack:

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

Meine Lösung

Ich habe einfach Dinge auf die altmodische Art gemacht ...

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
                }
            }
        }
    }
}

Akzeptierte Antwort

Verwenden Sie die XPath-Funktionen contains() oder starts-with() . Sie benötigen einen XPath-Ausdruck wie

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

oder vielleicht

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

die tatsächlich Attributknoten abruft. Oben ist das @* das Attribut-Platzhalterzeichen, nach dem Sie gesucht haben.


Beliebte Antwort

Verwenden Sie statt name() local-name() wie:

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

Der Unterschied ist, dass name() Ihnen den Attributnamen mit einem Präfix wie einem Namespace in xml geben sollte und local-name() wird dieses Präfix ausgeben, wenn es dort ist, in Ihrem Fall name() und local-name() sollten funktionieren auf die gleiche Weise, weil es html ist und es keine Namespaces gibt, aber es scheint, dass sie es nicht tun und es ist wahrscheinlich ein Bug.

Prüfung:

    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



Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum