【问题标题】:Find Multiple Tables using HTML Agility Pack使用 HTML Agility Pack 查找多个表
【发布时间】:2020-01-17 13:18:24
【问题描述】:

我正在尝试从以下网站查找第二个表格““Team and Opponent Stats”。

https://www.basketball-reference.com/teams/BOS/2017.html

但我的代码只显示第一个表。我尝试过各种 XPath 组合,例如 "//table[@id='DataTables_Table_0']/tr/td" ,但似乎没有任何效果。

这是我的代码:

var url = "https://www.basketball-reference.com/teams/BOS/2017.html";
        var web = new HtmlWeb();
        var doc = web.Load(url);

        var table1 = doc.DocumentNode
                     .Descendants("tr")
                     .Select(n => n.Elements("td").Select(p => p.InnerText).ToArray());

        foreach (string[] s in table1)
        {
            foreach (string str in s)
            {
                Console.WriteLine(str.ToString());
            }
            //Console.WriteLine(s);
        }

        foreach (var cell in doc.DocumentNode.SelectNodes("//table[@id='DataTables_Table_0']/tr/td"))
        {
            Console.WriteLine(cell.InnerText);
        }

这是我修改后的代码:

 foreach (HtmlNode tr in doc.DocumentNode.SelectNodes("//table[@id=\"team_and_opponent\"]//tbody"))
        {
            //looping on each row, get col1 and col2 of each row
            HtmlNodeCollection tds = tr.SelectNodes("td");
            for (int i = 0; i < tds.Count; i++)
            {
                Console.WriteLine(tds[i].InnerText);
            }
        }

这是我要抓取的网站部分的 html 代码。

  <div class="table_outer_container">
      <div class="overthrow table_container" id="div_team_and_opponent">
  <table class="suppress_all stats_table" id="team_and_opponent" data-cols-to-freeze="1"><caption>Team and Opponent Stats Table</caption>
   <colgroup><col><col><col><col><col><col><col><col><col><col><col><col><col><col><col><col><col><col><col><col><col><col><col><col></colgroup>
   <thead>      
      <tr>
         <th aria-label="&nbsp;" data-stat="player" scope="col" class=" poptip sort_default_asc center">&nbsp;</th>
         <th aria-label="Games" data-stat="g" scope="col" class=" poptip sort_default_asc center" data-tip="Games">G</th>
 

这是我用来获取正确表的最新 Agility Pack 代码。

 foreach (HtmlNode table in doc.DocumentNode.SelectNodes("//*[@id=\"team_and_opponent\"]"))
        {
            string tempStr = table.InnerText;

            foreach (HtmlNode nodecol in table.SelectNodes("//tr"))  ///html/body/div[1]/div[2]/div[2]/div/div/div[3]/table[2]/tbody[2]
            {
                foreach (HtmlNode cell in nodecol.SelectNodes("th|td"))
                {
                    Console.WriteLine("cell: " + cell.InnerHtml.ToString());

我仍然收到 NullReference 错误消息。

【问题讨论】:

  • 如果我在页面上进行“查看源代码”(注意这与使用开发工具进行检查有很大不同),数据似乎在备注中。不知道是正常的还是故意的(防刮?),但是HAP不会得到备注中的任何节点。

标签: c# xpath html-agility-pack


【解决方案1】:

检查您要查找的第二个表的 ID。通常,ID 在 DOM 中是唯一的。因此,如果第一个表名为“DataTables_Table_0”,则您尝试检索的另一个表的 Id 可能为“DataTables_Table_1”或类似的东西。查看页面的来源。

【讨论】:

  • dscore,根据上述修改了我的代码,但仍然出现空引用错误。
  • 您需要访问您尝试从中抓取数据的站点,并实际查看页面的 html。在 html 源代码中找到您尝试从其中抓取的第二个表,并查看它的 id。我只是推测它可能是DataTables_Table_1。我没有亲自查看html。如果您使用的是 Chrome,请右键单击 > 检查,这样可以更轻松地找到您要查找的表。我还要提一下,我认为网络抓取在某种程度上是一个灰色地带,请确保您没有违反任何网站的规则/政策。
  • 我已将 html 添加到原始帖子中。也许有某种线索可以让它发挥作用。 Tks
【解决方案2】:

该表格似乎最初是作为注释加载的,然后使用 Javascript 使其可见。

您应该在评论的 xpath (//*[@id="all_team_and_opponent"]/comment()) 上使用类似 SelectSingleNode 的东西并获取变量的 InnerHtml,然后您只需通过删除评论标签将其变成可见表。


我制作了一个非常简单的版本,并将其作为Gist 上传,因此您可以简单地检查我的解决方案并将其集成到您的程序中或在dotnetfiddle.net 上进行测试。


但是,如果您需要运行任何 JS 文件,您可以使用以下任何东西:

WebBrowser Class

与 HTML Agility Pack 混合使用时提取文本应该相当容易,尽管对于图像或其他元素类型可能会比较棘手。总体而言,它提供了不错的性能。

Javascript.Net

它允许您使用 Chrome 的 V8 JavaScript 引擎执行脚本。您只需要找出哪些文件会更改内容。

Selenium

您可以为您喜欢的浏览器(Chrome、Firefox、PhantomJS)使用 Selenium+一个网络驱动程序。它有点慢,但非常灵活。这可能是矫枉过正,所以我推荐上述任何选项。

【讨论】:

    【解决方案3】:

    这是一个动态网页(由客户端 javascript 操作),因此您从服务器下载并在 HtmlAgilityPack 中看到的内容与您最终在浏览器中看到的内容不匹配。该表实际上是作为评论从服务器返回的。幸运的是,评论具有该表格的完整标记,因此您真正需要做的就是选择评论,去掉文本的评论部分,将其解析为 html,然后像往常一样选择。

    因此,例如,如果您想将其加载到数据表中,您可以这样做:

    var url = "https://www.basketball-reference.com/teams/BOS/2017.html";
    var web = new HtmlWeb();
    var doc = web.Load(url);
    var tableComment = doc.DocumentNode
        .SelectSingleNode("//div[@id='all_team_and_opponent']/comment()");
    var table = HtmlNode.CreateNode(tableComment.OuterHtml[4..^3])
        .SelectSingleNode("//table[@id='team_and_opponent']");
    var dataTable = ToDataTable(table);
    
    DataTable ToDataTable(HtmlNode node)
    {
        var dt= new DataTable();
        dt.BeginInit();
        foreach (var col in node.SelectNodes("thead/tr/th"))
            dt.Columns.Add(col.GetAttributeValue("aria-label", ""), typeof(string));
        dt.EndInit();
        dt.BeginLoadData();
        foreach (var row in node.SelectNodes("tbody/tr"))
            dt.Rows.Add(row.SelectNodes("th|td").Select(t => t.InnerText).ToArray());
        dt.EndLoadData();
        return dt;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-01-07
      • 1970-01-01
      • 2011-04-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多