【问题标题】:Parsing dl with HtmlAgilityPack使用 HtmlAgilityPack 解析 dl
【发布时间】:2012-02-15 02:07:22
【问题描述】:

这是我尝试在 ASP.Net (C#) 中使用 Html Agility Pack 解析的示例 HTML。

<div class="content-div">
    <dl>
        <dt>
            <b><a href="1.html" title="1">1</a></b>
        </dt>
        <dd> First Entry</dd>
        <dt>
            <b><a href="2.html" title="2">2</a></b>
        </dt>
        <dd> Second Entry</dd>
        <dt>
            <b><a href="3.html" title="3">3</a></b>
        </dt>
        <dd> Third Entry</dd>
    </dl>
</div>

我想要的价值观是:

  • 超链接 -> 1.html
  • 锚文本 ->1
  • 内部文本 od dd -> 第一个条目

(我在这里以第一个条目为例,但我想要列表中所有条目的这些元素的值)

这是我目前使用的代码,

var webGet = new HtmlWeb();
            var document = webGet.Load(url2);
var parsedValues=
   from info in document.DocumentNode.SelectNodes("//div[@class='content-div']")
   from content in info.SelectNodes("dl//dd")
   from link in info.SelectNodes("dl//dt/b/a")
       .Where(x => x.Attributes.Contains("href"))
   select new 
   {
       Text = content.InnerText,
       Url = link.Attributes["href"].Value,
       AnchorText = link.InnerText,
   };

GridView1.DataSource = parsedValues;
GridView1.DataBind();

问题是我正确地获得了链接和锚文本的值,但是对于它的内部文本,它只取第一个条目的值,并为所有其他条目填充相同的值,总次数元素发生,然后从第二个开始。我的解释可能不太清楚,所以这是我使用此代码得到的示例输出:

First Entry     1.html  1
First Entry     2.html  2
First Entry     3.html  3
Second Entry    1.html  1
Second Entry    2.html  2
Second Entry    3.html  3
Third Entry     1.html  1
Third Entry     2.html  2
Third Entry     3.html  3

而我试图得到

First Entry      1.html     1
Second Entry     2.html     2
Third Entry      3.html     3

我对 HAP 很陌生,对 xpath 知之甚少,所以我确信我在这里做错了,但即使花了几个小时我也无法让它工作。任何帮助将不胜感激。

【问题讨论】:

    标签: c# asp.net screen-scraping html-agility-pack


    【解决方案1】:

    解决方案 1

    我已经定义了一个函数,给定一个dt 节点将返回它之后的下一个dd 节点:

    private static HtmlNode GetNextDDSibling(HtmlNode dtElement)
    {
        var currentNode = dtElement;
    
        while (currentNode != null)
        {
            currentNode = currentNode.NextSibling;
    
            if(currentNode.NodeType == HtmlNodeType.Element && currentNode.Name =="dd")
                return currentNode;
        }
    
        return null;
    }
    

    现在可以将 LINQ 代码转换为:

    var parsedValues =
        from info in document.DocumentNode.SelectNodes("//div[@class='content-div']")
        from dtElement in info.SelectNodes("dl/dt")
        let link = dtElement.SelectSingleNode("b/a[@href]")
        let ddElement = GetNextDDSibling(dtElement)
        where link != null && ddElement != null
        select new
        {
            Text = ddElement.InnerHtml,
            Url = link.GetAttributeValue("href", ""),
            AnchorText = link.InnerText
        };
    

    解决方案 2

    无附加功能:

    var infoNode = 
            document.DocumentNode.SelectSingleNode("//div[@class='content-div']");
    
    var dts = infoNode.SelectNodes("dl/dt");
    var dds = infoNode.SelectNodes("dl/dd");
    
    var parsedValues = dts.Zip(dds,
        (dt, dd) => new
        {
            Text = dd.InnerHtml,
            Url = dt.SelectSingleNode("b/a[@href]").GetAttributeValue("href", ""),
            AnchorText = dt.SelectSingleNode("b/a[@href]").InnerText
        });
    

    【讨论】:

    • 非常感谢,这行得通。但是我不完全明白这里发生了什么。可以在没有函数的情况下使用某种 xpath/regex 来完成吗?这个解决方案对我来说非常好,我只是好奇它是如何工作的。如果您能给出一个简短的解释,那就太好了,谢谢。
    • 问题是我们需要类似“给定这个dt标签,给我紧跟在它后面的dd标签”。我试图通过将 ddElement 选择为 dtElement.NextSibling 来使其更简单,但 HtmlAgilityPack 认为下一个节点是 &lt;dt&gt;&lt;dd&gt; 标记之间的空白。这让我想到了你在上面看到的稍微难看的解决方案。
    • 我刚刚想出了另一个不涉及声明函数的可能解决方案 - 请参阅更新后的答案。
    • 谢谢,我现在明白了。我也尝试了您的第二个解决方案,但我收到一个错误(如下:),是否需要添加任何引用才能使第二个解决方案正常工作。错误:“HtmlAgilityPack.HtmlNodeCollection”不包含“Zip”的定义,并且找不到接受“HtmlAgilityPack.HtmlNodeCollection”类型的第一个参数的扩展方法“Zip”(您是否缺少 using 指令或程序集引用?)
    • Zip 是一个 Linq 扩展功能。您使用的是哪个 .Net 版本?
    【解决方案2】:

    只是一个例子如何使用Html Agility Pack解析一些元素

    public string ParseHtml()
    {
        string output = null;
        HtmlDocument htmldocument = new HtmlDocument();
        htmldocument.LoadHtml(YourHTML);
    
        HtmlNode node = htmldocument.DocumentNode;    
    
        HtmlNodeCollection dds = node.SelectNodes("//dd"); //Select all dd tags
        HtmlNodeCollection anchors = node.SelectNodes("//b/a[@href]"); //Select all 'a' tags that contais href attribute
    
        for (int i = 0; i < dds.Count; i++)
        {
            string atributteValue = null.
            Text = dds[i].InnerText;
            Url = anchors[i].GetAttributeValue("href", atributteValue);
            AnchorText = anchors[i].InnerText;
    
            //Your code...
        }
        return output;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-26
      • 2016-11-20
      • 2010-12-03
      • 2015-03-31
      • 1970-01-01
      相关资源
      最近更新 更多