Ich verwende HtmlAgilityPack, um das HTML-Dokument eines Webbrowser-Steuerelements zu analysieren. Ich bin in der Lage, meinen gewünschten HtmlNode zu finden, aber nachdem ich den HtmlNode bekommen habe, möchte ich das entsprechende HtmlElement im WebbrowserControl.Document wiederholen.
Tatsächlich analysiert HtmlAgilityPack eine Offline-Kopie des Live-Dokuments, während ich auf Live-Elemente des Webbrowser- currentStyle
runtimeStyle
möchte, um auf einige gerenderte Attribute wie currentStyle
oder runtimeStyle
HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
doc.LoadHtml(webBrowser1.Document.Body.InnerHtml);
var some_nodes = doc.DocumentNode.SelectNodes("//p");
// this selection could be more sophisticated
// and the answer shouldn't relay on it.
foreach (HtmlNode node in some_nodes)
{
HtmlElement live_element = CorrespondingElementFromWebBrowserControl(node);
// CorrespondingElementFromWebBrowserControl is what I am searching for
}
Wenn das Element ein bestimmtes Attribut hätte, könnte es einfach sein, aber ich möchte eine Lösung, die auf jedem Element funktioniert.
Bitte helfen Sie mir, was ich dagegen tun kann.
Das XPath
Attribut von HtmlAgilityPack.HtmlNode
zeigt die Knoten auf dem Pfad vom Stamm zum Knoten an. Zum Beispiel \div[1]\div[2]\table[0]
. Sie können diesen Pfad im Live-Dokument durchlaufen, um das entsprechende Live-Element zu finden. Dieser Pfad ist jedoch möglicherweise nicht präzise, da HtmlAgilityPack einige Tags wie <form>
bevor Sie diese Lösung verwenden und die weggelassenen Tags wieder hinzufügen
HtmlNode.ElementsFlags.Remove("form");
struct DocNode
{
public string Name;
public int Pos;
}
///// structure to hold the name and position of each node in the path
Die folgende Methode findet das Live-Element gemäß dem XPath
static public HtmlElement GetLiveElement(HtmlNode node, HtmlDocument doc)
{
var pattern = @"/(.*?)\[(.*?)\]"; // like div[1]
// Parse the XPath to extract the nodes on the path
var matches = Regex.Matches(node.XPath, pattern);
List<DocNode> PathToNode = new List<DocNode>();
foreach (Match m in matches) // Make a path of nodes
{
DocNode n = new DocNode();
n.Name = n.Name = m.Groups[1].Value;
n.Pos = Convert.ToInt32(m.Groups[2].Value)-1;
PathToNode.Add(n); // add the node to path
}
HtmlElement elem = null; //Traverse to the element using the path
if (PathToNode.Count > 0)
{
elem = doc.Body; //begin from the body
foreach (DocNode n in PathToNode)
{
//Find the corresponding child by its name and position
elem = GetChild(elem, n);
}
}
return elem;
}
der oben verwendete Code für die GetChild-Methode
public static HtmlElement GetChild(HtmlElement el, DocNode node)
{
// Find corresponding child of the elemnt
// based on the name and position of the node
int childPos = 0;
foreach (HtmlElement child in el.Children)
{
if (child.TagName.Equals(node.Name,
StringComparison.OrdinalIgnoreCase))
{
if (childPos == node.Pos)
{
return child;
}
childPos++;
}
}
return null;
}
HtmlAgilityPack kann definitiv keinen Zugriff auf Knoten in Live-HTML direkt bereitstellen. Da Sie gesagt haben, dass es auf dem Element keinen eindeutigen Stil / Klasse / ID gibt, müssen Sie manuell durch die Knoten gehen und Übereinstimmungen finden.
Unter der Annahme, dass HTML einigermaßen gültig ist (so dass sowohl Browser als auch HtmlAgilityPack die Normalisierung in ähnlicher Weise durchführen), können Sie Paare von Elementen durchlaufen, die von der Wurzel beider Bäume ausgehen und denselben Kindknoten auswählen.
Grundsätzlich können Sie "positionsbasierte" XPath-Knoten in einem Baum erstellen und in einem anderen Baum auswählen. Xpath würde ungefähr so aussehen (abhängig davon, ob Sie nur auf Positionen oder Position und Knotennamen achten möchten):
"/*[1]/*[4]/*[2]/*[7]"
"/body/div[2]/span[1]/p[3]"
Schritte:
HtmlNode
Sie HtmlNode
, haben Sie alle übergeordneten Knoten bis zum Stamm gefunden.