HtmlAgilityPack并选择节点和子节点

时间:2013-02-21 13:24:47

标签: c# xpath html-agility-pack

希望有人可以帮助我。

假设我有一个包含多个div的html文档,如下例所示:

<div class="search_hit">

    <span prop="name">Richard Winchester</span>
    <span prop="company">Kodak</span>
    <span prop="street">Arlington Road 1</span>

</div>
<div class="search_hit">

    <span prop="name">Ted Mosby</span>
    <span prop="company">HP</span>
    <span prop="street">Arlington Road 2</span>

</div>

我正在使用HtmlAgilityPack来获取html文档。我需要知道的是我如何获得每个“search_hit”-div?

的跨度

我的第一个想法是这样的:

foreach (HtmlAgilityPack.HtmlNode node in doc.DocumentNode.SelectNodes("//div[@class='search_hit']"))
{
     foreach (HtmlAgilityPack.HtmlNode node2 in node.SelectNodes("//span[@prop]"))
     {

     }
}

每个div应该是包含spans作为属性的对象。 I. e。

public class Record
    {
        public string Name { get; set; }
        public string company { get; set; }
        public string street { get; set; }
    }

然后填写此清单:

public List<Record> Results = new List<Record>();

但我正在使用的XPATH并没有在子节点中进行搜索,因为它应该这样做。它会一次又一次地搜索整个文档。

我的意思是我已经让它以这种方式工作,我只是得到整个页面的跨度。但后来我在跨度和div之间没有任何关系。意思是:我不知道哪个范围与哪个div有关。

有人知道解决方案吗?我已经玩了很多,我现在完全糊涂了。)

感谢任何帮助!

5 个答案:

答案 0 :(得分:52)

如果您使用//,则会从文档开始搜索。

使用.//从当前节点

中搜索所有内容
 foreach (HtmlAgilityPack.HtmlNode node2 in node.SelectNodes(".//span[@prop]"))

或完全删除前缀以搜索直接子项:

 foreach (HtmlAgilityPack.HtmlNode node2 in node.SelectNodes("span[@prop]"))

答案 1 :(得分:31)

以下适用于我。重要的是就像BeniBela指出在第二次调用'SelectNodes'时添加一个点。

List<Record> lstRecords=new List<Record>();
foreach (HtmlNode node in doc.DocumentNode.SelectNodes("//div[@class='search_hit']"))
{
  Record record=new Record();
  foreach (HtmlNode node2 in node.SelectNodes(".//span[@prop]"))
  {
    string attributeValue = node2.GetAttributeValue("prop", "");
    if (attributeValue == "name")
    {
      record.Name = node2.InnerText;
    }
    else if (attributeValue == "company")
    {
      record.company = node2.InnerText;
    }
    else if (attributeValue == "street")
    {
      record.street = node2.InnerText;
    }
  }
  lstRecords.Add(record);
}

答案 2 :(得分:3)

首先,请看一下: Html Agility Pack - Problem selecting subnode

以下是针对您的问题的完整解决方案:

IList<Record> results = new List<Record>();
foreach (var node in doc.DocumentNode.SelectNodes("//div[@class='search_hit']")) {
    var record = new Record();
    record.Name = node.SelectSingleNode(".//span[@prop='name']").InnerText;
    record.company = node.SelectSingleNode(".//span[@prop='company']").InnerText;
    record.street = node.SelectSingleNode(".//span[@prop='street']").InnerText;
    results.Add(record);
}

如果您阅读了我指出的问题,您会看到执行./span[@prop='name']完全相同,因为那些span节点是div节点的(直接)子节点。


如果span个节点没有prop个属性,并且您希望根据它们的显示顺序进行分配,则可以执行以下操作:

foreach (var node in doc.DocumentNode.SelectNodes("//div[@class='search_hit']")) {
    var spanNodes = node.SelectNodes("./span");
    var record = new Record();
    record.Name = spanNodes[0].InnerText;
    record.company = spanNodes[1].InnerText;
    record.street = spanNodes[2].InnerText;
    results.Add(record);
}

答案 3 :(得分:2)

对我感到羞耻:)

你们所有人都是对的。

我发现了问题。这个NullReferenceException一直在唠叨我,所以我花了更多时间详细研究它。 在所有这些div之间有一个div具有相同的“class ='search-hit'”属性但没有内部的跨度。这就是为什么它在第二次循环中通过错误。

foreach (HtmlAgilityPack.HtmlNode node in doc.DocumentNode.SelectNodes("//span[@prop]/ancestor::div[@class='search_hit']"))
   {
        Record rec = new Record();
        foreach (HtmlAgilityPack.HtmlNode node2 in node.SelectNodes(".//span[@prop]"))
           {
           }
           rList.Results.Add(rec);
   }

上面的代码正在运作。

谢谢你们的时间和帮助!

答案 4 :(得分:0)

我用过它。 class convert id

  HtmlNodeCollection nodes = dokuman.DocumentNode.SelectNodes("//div[@id='search_hit']//span[@prop]");


            for (int i = 0; i < nodes .Count; i++)
        {
            var record = new Record();


                record.Name = links[i].InnerText;   results.Add(record);  }