HTML Agility pack - 解析表格

HTML Agility pack - parsing tables

提问人:weismat 提问时间:3/18/2009 最后编辑:Nathaniel Fordweismat 更新时间:4/27/2019 访问量:115863

问:

我想使用 HTML 敏捷包来解析复杂网页中的表,但我不知何故迷失在对象模型中。

我查看了链接示例,但没有找到任何以这种方式的表数据。 我可以使用 XPath 来获取表吗?在加载了有关如何获取表格的数据后,我基本上迷路了。我以前在Perl中做过这个,它有点笨拙,但有效。().HTML::TableParser

如果能阐明解析的正确对象顺序,我也很高兴。

c# html 解析 html-agility-pack

评论


答:

128赞 Marc Gravell 3/18/2009 #1

像这样的东西怎么样: 使用 HTML Agility Pack

HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(@"<html><body><p><table id=""foo""><tr><th>hello</th></tr><tr><td>world</td></tr></table></body></html>");
foreach (HtmlNode table in doc.DocumentNode.SelectNodes("//table")) {
    Console.WriteLine("Found: " + table.Id);
    foreach (HtmlNode row in table.SelectNodes("tr")) {
        Console.WriteLine("row");
        foreach (HtmlNode cell in row.SelectNodes("th|td")) {
            Console.WriteLine("cell: " + cell.InnerText);
        }
    }
}

请注意,如果需要,可以使用 LINQ-to-Objects 使它更漂亮:

var query = from table in doc.DocumentNode.SelectNodes("//table").Cast<HtmlNode>()
            from row in table.SelectNodes("tr").Cast<HtmlNode>()
            from cell in row.SelectNodes("th|td").Cast<HtmlNode>()
            select new {Table = table.Id, CellText = cell.InnerText};

foreach(var cell in query) {
    Console.WriteLine("{0}: {1}", cell.Table, cell.CellText);
}

评论

1赞 Johnny_D 7/16/2012
嗨,马克,您能提供有关解析大文件的建议吗?超过 50 mb 的文件,我无法获取较大文件上的子 tr 节点。
0赞 Dark_Knight 1/13/2017
@Marc - 如果表格是分页的,那么如何通过抓取进入下一页?
0赞 Marc Gravell 1/13/2017
@Dark_Knight您需要点击原始页面使用的任何 ajax 路由
0赞 Dark_Knight 1/19/2017
@MarcGravell我找到了这个,你知道如何调用那个函数吗?paging_init('sites', 'sites_tbl','/ipID/23.227.38.0/ipIDii/23.227.38.255/sort/6/asc/1', true, '1', '536', {sortCol: '6', sortAsc: '1'})
30赞 Coda 6/24/2010 #2

我发现获取特定元素的 XPath 的最简单方法是安装Firefox的FireBug扩展,转到站点/网页,按F12调出Firebug;右键单击要查询的页面上的元素,然后选择“检查元素” Firebug 将在其 IDE 中选择该元素,然后在 Firebug 中右键单击该元素并选择“复制 XPath” 此函数将为您提供使用 HTML Agility Library 获取所需元素所需的确切 XPath 查询。

评论

4赞 Anders 9/26/2011
请记住,有时浏览器会稍微更改 html 的 DOM——比如将 <tbody> 添加到<表中>如果它丢失了。默认情况下,Html Agility Pack 在解析 html 时也不会包含 <form> 和 <option> 标签。记住这些差异,您将在浏览器和 Html Agility Pack 之间的 XPath 兼容性方面取得更大的成功。
0赞 Phill Healey 1/3/2015
即使承认安德斯提到的陷阱,这也是一个很好的节省时间。
0赞 Noctis 9/10/2017
似乎Firefox不再支持它:(
-1赞 rk42 1/13/2016 #3

上面的行回答:

HtmlDocument doc = new HtmlDocument();

这在 VS 2015 C# 中不起作用。你不能再构造一个了。HtmlDocument

另一个 MS“功能”使事情更难使用。尝试查看此链接以获取一些示例代码。HtmlAgilityPack.HtmlWeb

评论

1赞 Peroxy 1/15/2018
对我有用,不知道你在说什么。
1赞 Shibumi Tait 5/5/2017 #4

就我而言,有一个表恰好是来自路由器的设备列表。如果您希望使用 TR/TH/TD(行、标题、数据)而不是上面提到的矩阵来读取表格,您可以执行以下操作:

    List<TableRow> deviceTable = (from table in document.DocumentNode.SelectNodes(XPathQueries.SELECT_TABLE)
                                       from row in table?.SelectNodes(HtmlBody.TR)
                                       let rows = row.SelectSingleNode(HtmlBody.TR)
                                       where row.FirstChild.OriginalName != null && row.FirstChild.OriginalName.Equals(HtmlBody.T_HEADER)
                                       select new TableRow
                                       {
                                           Header = row.SelectSingleNode(HtmlBody.T_HEADER)?.InnerText,
                                           Data = row.SelectSingleNode(HtmlBody.T_DATA)?.InnerText}).ToList();
                                       }  

TableRow 只是一个简单的对象,将 Header 和 Data 作为属性。 该方法处理 null 和以下情况:

<tr>
    <td width="28%">&nbsp;</td>
</tr>

这是没有标题的行。带有常量悬挂的 HtmlBody 对象可能很容易推断出来,但我仍然为此道歉。我来自这个世界,如果你的代码中有“,它应该是恒定的或可本地化的。

3赞 B. Miller 4/27/2019 #5

我知道这是一个很古老的问题,但这是我的解决方案,它有助于可视化表格,以便您可以创建类结构。这也是使用 HTML Agility Pack

HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(@"<html><body><p><table id=""foo""><tr><th>hello</th></tr><tr><td>world</td></tr></table></body></html>");
var table = doc.DocumentNode.SelectSingleNode("//table");
var tableRows = table.SelectNodes("tr");
var columns = tableRows[0].SelectNodes("th/text()");
for (int i = 1; i < tableRows.Count; i++)
{
    for (int e = 0; e < columns.Count; e++)
    {
        var value = tableRows[i].SelectSingleNode($"td[{e + 1}]");
        Console.Write(columns[e].InnerText + ":" + value.InnerText);
    }
Console.WriteLine();
}