【问题标题】:Gettig Htmlelement based on HtmlAgilityPack.HtmlNode基于 HtmlAgilityPack.HtmlNode 获取 Html 元素
【发布时间】:2015-01-05 02:00:33
【问题描述】:

我使用 HtmlAgilityPack 来解析 webbrowser 控件的 html 文档。 我能够找到我想要的 HtmlNode,但是在获得 HtmlNode 之后,我想在 WebbrowserControl.Document 中重新调整相应的 HtmlElement。

事实上,HtmlAgilityPack 会解析实时文档的离线副本,而我想访问 webbrowser 控件的实时元素以访问一些呈现的属性,例如 currentStyleruntimeStyle

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
}

如果元素具有特定属性,这可能很容易,但我想要一个适用于任何元素的解决方案。

请帮我解决一下。

【问题讨论】:

  • 控制文档中返回对应的htmlnode是什么意思?哪个节点是匹配的,您希望以哪种方式显示它?
  • @Xenogenesis 我在问题中提供了一个代码来表达我的意思。
  • 问题是你必须指定你需要的元素应该匹配哪些搜索条件有了这个条件,例如所有作为链接或元素名称中包含“k”的子节点,你可以使用通过item的子节点的递归循环
  • @Xenogenesis 我编辑了我的问题,它不是关于匹配,只是对应的元素。我可以在 HtmlAgilityPack 中找到任何元素,但我需要一个指向 Web 浏览器控件中真实元素的指针,我想知道为什么 HtmlAgilityPack 本身不提供这种关系!它只是以htmlNode的格式处理控件的html的离线副本!
  • 举例说明您的 HTML 以及您尝试获取的节点 .. 这将有助于我们为您提供帮助

标签: c# html webbrowser-control html-agility-pack


【解决方案1】:

事实上,直接在网络浏览器控件中更改文档似乎没有直接的可能性。 但是您可以从中提取 html,对其进行操作并重新写回,如下所示:

HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
doc.LoadHtml(webBrowser1.DocumentText);

foreach (HtmlAgilityPack.HtmlNode node in doc.DocumentNode.ChildNodes) {
    node.Attributes.Add("TEST", "TEST");
}

StringBuilder sb = new StringBuilder();
using (StringWriter sw = new StringWriter(sb)) {
    doc.Save(sw);
    webBrowser1.DocumentText = sb.ToString();
}

对于直接操作,您可以使用指向文档的非托管指针 webBrowser1.Document.DomDocument,但这超出了我的知识范围。

【讨论】:

  • 还是谢谢你,你没有在代码中提到sb和节点之间的关系......但是你的意思是给节点添加一个特定的属性,然后写回去并根据搜索那个属性?但是目前我以类似的方式解决了我的问题,我想获取所选节点的运行时字体,现在我通过在 Web 浏览器控件中搜索节点的内部文本来做到这一点...
【解决方案2】:

HtmlAgilityPack 绝对不能直接提供对实时 HTML 中节点的访问。既然你说元素上没有不同的样式/类/id,你必须手动遍历节点并找到匹配项。

假设 HTML 是合理有效的(因此浏览器和 HtmlAgilityPack 都执行类似的规范化),您可以从两棵树的根开始遍历元素对并选择相同的子节点。

基本上,您可以将“基于位置”的 XPath 构建到一棵树中的节点并在另一棵树中选择它。 Xpath 看起来像(取决于你想只关注位置或位置和节点名称):

 "/*[1]/*[4]/*[2]/*[7]"
 "/body/div[2]/span[1]/p[3]"

步骤:

  1. 在使用HtmlNode 时,您会发现收集所有父节点直至根节点。
  2. 在浏览器中获取 HTML 元素的根
  3. 对于每个级别的子级,在其父级的第 1 步中找到相应子级在 HtmlNodes 集合上的位置,然后在当前活动节点的子级中找到活动 HtmlElement。
  4. 移动到新找到的子节点并返回到 3 直到找到您正在寻找的节点。

【讨论】:

  • 谢谢,我会检查你的答案,除了你可以查看我编辑的问题来投票,我修改它并提到我的真正目的是访问 live 元素的 currentStyle。
  • 我将您的建议实现为以下答案!
【解决方案3】:

HtmlAgilityPack.HtmlNodeXPath 属性显示从根到节点的路径上的节点。例如\div[1]\div[2]\table[0]。你可以在直播文档中遍历这条路径,找到对应的直播元素。但是,此路径可能不精确,因为 HtmlAgilityPack 删除了一些标签,如 <form> 然后在使用此解决方案之前使用

添加省略的标签
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

下面的方法根据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;
    }

上面使用的GetChild方法的代码

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-07-10
    • 1970-01-01
    • 1970-01-01
    • 2011-03-13
    • 1970-01-01
    • 2023-02-07
    • 2016-10-07
    相关资源
    最近更新 更多