C# 使用html敏捷包和Linq解析html

C# 使用html敏捷包和Linq解析html,c#,linq,html-parsing,html-agility-pack,C#,Linq,Html Parsing,Html Agility Pack,我有下面的HTML (..) <tbody> <tr> <td class="name"> Test1 </td> <td class="data"> Data </td> <td class="data2"> Data 2 </td> </tr> <tr> <td class="name"> Test2 </td> <t

我有下面的HTML

(..)
<tbody>
 <tr>
  <td class="name"> Test1 </td>
  <td class="data"> Data </td>
  <td class="data2"> Data 2 </td>
 </tr>
 <tr>
  <td class="name"> Test2 </td>
  <td class="data"> Data2 </td>
  <td class="data2"> Data 2 </td>
 </tr>
</tbody>
(..)

但是当我试图查看
数据时,我得到了
{“对象引用未设置为对象的实例”。}
这里有一种方法-首先将所有数据解析为数据结构,然后读取它。这有点混乱,当然需要更多的验证,但下面是:

HtmlWeb hw = new HtmlWeb();
HtmlDocument doc = hw.Load("http://jsbin.com/ezuge4");
HtmlNodeCollection nodes = doc.DocumentNode
                              .SelectNodes("//table[@id='MyTable']//tr");
var data = nodes.Select(
    node => node.Descendants("td")
        .ToDictionary(descendant => descendant.Attributes["class"].Value,
                      descendant => descendant.InnerText.Trim())
        ).ToDictionary(dict => dict["name"]);
string test1Data = data["Test1"]["data"];

在这里,我将每个
转换为一个字典,其中
的类是一个键,文本是一个值。接下来,我将字典列表转换为字典字典(提示-将其抽象掉),其中每个
名称是关键。

至于您的尝试,您的代码有两个问题:

  • ChildNodes
    很奇怪-它还返回空白文本节点,这些节点没有
    属性(当然不能有属性)
  • 正如詹姆斯·沃尔福德(James Walford)所评论的,文本周围的空格很重要,您可能需要对其进行修剪
  • 通过这两项更正,以下工作:

    var data =
          from tr in doc.DocumentNode.Descendants("tr")
          from td in tr.Descendants("td").Where(x => x.Attributes["class"].Value == "name")
         where td.InnerText.Trim() == "Test1"
        select tr;
    
    而不是

    td.InnerText == "Test1"
    
    试一试


    下面是XPATH方法-嗯。。。如今,每个人似乎都忘记了XPATH的强大功能,只专注于C#XLinq:-)

    此函数用于获取与名称关联的所有数据值:

    public static IEnumerable<string> GetData(HtmlDocument document, string name)
    {
        return from HtmlNode node in
            document.DocumentNode.SelectNodes("//td[@class='name' and contains(text(), '" + name + "')]/following-sibling::td")
            select node.InnerText.Trim();
    }
    

    我可以推荐两种方法之一:

    ,它将html转换为有效的xml,然后可以使用OOTB Linq对其进行查询

    或者


    Linq to HTML(),虽然没有在CodePlex上维护(这是一个提示,Keith),但它为springboard提供了一组合理的功能。

    确切地说,您想做什么?你不想要的代码是什么?呵呵,对不起,我忘记添加了,我现在正在添加。你能告诉我们你的错误是什么吗?或者你期望发生的事情没有发生?我已经改变了我的问题,希望能让它更容易理解。在你的例子中,tds中的文本有一个前后空白,而你要查找的字符串没有。我想到了一个带有
    的xpath包含
    ,但它确实存在一个主要问题:搜索
    Test1
    也会找到
    Test10
    NotTest1
    等等。我真的不知道足够的xpath来解决这个问题…@Kobi-如果你不想使用contains,那么你可以使用=。如果空格是一个问题,可以使用normalize space删除它们,否则这个链接会提供更多信息:我之所以喜欢Linq答案而不是XPath,是因为后者很难阅读和理解。前者非常清楚其目的,如果需要,可以将查询分解为子查询进行调试。XPath迟钝,无法调试。如果没有大量的测试数据,很难验证它是否做了正确的事情。仅仅通过谷歌搜索XPath语法的权威页面是一件令人讨厌的琐事。我仍然喜欢HAP,但每次我看到XPath语句我都会退缩。当你不知道的时候,一切都很困难。我认为XPATH在查询XML集时更易于使用和理解。它还优雅地处理空值(与Linq不同)。唯一的(大)缺点是它不适合区分大小写的比较。另一个问题是XPATH不可移植(例如WinRT上不存在)。无论如何,使用你喜欢的库:-)相反,如果你理解它,一切看起来都很容易。这并不意味着对其他人来说很容易。LINQ有很多应用。XPATH没有。就我个人而言,我更愿意说.dependents(“a”).Last()而不是“//a[Last()]”,但既然我需要的是这件小事,我就告诉你我对xpath的记忆,并对你的答案进行投票。
    td.InnerText == " Test1 "
    
    d.InnerText.Trim() == "Test1"
    
    public static IEnumerable<string> GetData(HtmlDocument document, string name)
    {
        return from HtmlNode node in
            document.DocumentNode.SelectNodes("//td[@class='name' and contains(text(), '" + name + "')]/following-sibling::td")
            select node.InnerText.Trim();
    }
    
        HtmlDocument doc = new HtmlDocument();
        doc.Load(yourHtml);
    
        foreach (string data in GetData(doc, "Test2"))
        {
            Console.WriteLine(data);
        }