在.NET开发中处理HTML文档解析任务时,HtmlAgilityPack是常用的轻量级解析工具,它不需要文档完全符合XML规范,能够适配大多数实际场景下的HTML结构,尤其适合提取文档中的表格数据。

环境准备与基础配置
首先需要在项目中引入HtmlAgilityPack库,若使用NuGet包管理器,可以直接搜索安装HtmlAgilityPack包,安装完成后在代码文件顶部添加对应的命名空间引用。
using HtmlAgilityPack;
using System;
using System.Collections.Generic;
namespace HtmlTableParseDemo
{
class Program
{
static void Main(string[] args)
{
// 后续解析逻辑将在此处编写
Console.WriteLine("HtmlAgilityPack表格解析示例");
}
}
}
定位目标表格的常用方法
要精确解析指定表格,首先需要准确定位到目标表格节点,HtmlAgilityPack提供了多种定位方式,开发者可以根据实际HTML结构选择最合适的方法。
通过表格属性定位
如果目标表格带有唯一的id、class或者其他自定义属性,可以直接通过XPath表达式定位。例如目标表格的id为targetTable,定位代码如下:
// 加载HTML文档,这里以加载本地字符串为例,也可以加载本地文件或网络地址
string htmlContent = "<html><body><table id='targetTable'><tr><td>测试数据</td></tr></table></body></html>";
HtmlDocument htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(htmlContent);
// 通过id定位表格节点
HtmlNode targetTable = htmlDoc.DocumentNode.SelectSingleNode("//table[@id='targetTable']");
if (targetTable != null)
{
Console.WriteLine("成功定位到目标表格");
}
else
{
Console.WriteLine("未找到目标表格");
}
通过表格位置定位
如果表格没有唯一属性,也可以通过其在文档中的顺序定位,比如获取页面中第二个表格:
// 获取所有table节点,取索引为1的第二个表格(索引从0开始)
HtmlNodeCollection allTables = htmlDoc.DocumentNode.SelectNodes("//table");
if (allTables != null && allTables.Count > 1)
{
HtmlNode targetTable = allTables[1];
Console.WriteLine("通过位置定位到第二个表格");
}
通过表格内容特征定位
如果表格有固定的表头内容,也可以通过表头文本匹配定位,例如表头包含“用户姓名”的表格:
// 查找包含表头文本为用户姓名的表格
HtmlNode targetTable = null;
HtmlNodeCollection tables = htmlDoc.DocumentNode.SelectNodes("//table");
if (tables != null)
{
foreach (HtmlNode table in tables)
{
// 检查表格内是否存在包含用户姓名的th或td
HtmlNode headerCell = table.SelectSingleNode(".//th[contains(text(),'用户姓名')]");
if (headerCell != null)
{
targetTable = table;
break;
}
}
}
if (targetTable != null)
{
Console.WriteLine("通过内容特征定位到目标表格");
}
提取表格行列数据
定位到目标表格后,就可以按照表格结构逐行提取数据,通常表格的行用<tr>标签表示,表头用<th>标签,单元格用<td>标签。
提取表头数据
先提取表格的第一行作为表头,获取所有<th>或第一个<tr>下的<td>内容:
List<string> headers = new List<string>();
// 获取表格的第一行
HtmlNode firstRow = targetTable.SelectSingleNode(".//tr[1]");
if (firstRow != null)
{
// 优先获取th标签,没有则获取td标签
HtmlNodeCollection headerCells = firstRow.SelectNodes(".//th");
if (headerCells == null)
{
headerCells = firstRow.SelectNodes(".//td");
}
if (headerCells != null)
{
foreach (HtmlNode cell in headerCells)
{
// 去除空白字符后添加到表头列表
headers.Add(cell.InnerText.Trim());
}
}
}
Console.WriteLine("表头内容:" + string.Join(",", headers));
提取表格主体数据
提取完表头后,遍历剩余的行,获取每一行的单元格内容,整理成结构化的数据:
List<List<string>> tableData = new List<List<string>>();
// 获取表格所有行,跳过第一行表头
HtmlNodeCollection rows = targetTable.SelectNodes(".//tr");
if (rows != null)
{
for (int i = 1; i < rows.Count; i++)
{
HtmlNode row = rows[i];
List<string> rowData = new List<string>();
HtmlNodeCollection cells = row.SelectNodes(".//td");
if (cells != null)
{
foreach (HtmlNode cell in cells)
{
rowData.Add(cell.InnerText.Trim());
}
}
tableData.Add(rowData);
}
}
// 输出提取到的表格数据
Console.WriteLine("表格主体数据:");
foreach (List<string> row in tableData)
{
Console.WriteLine(string.Join("|", row));
}
常见异常处理与注意事项
在实际解析过程中,可能会遇到一些特殊情况,需要提前做好处理:
- 如果HTML文档结构不规范,部分<tr>或<td>标签缺失,需要在提取时做空值判断,避免索引越界。
- 表格内如果包含嵌套表格,上述直接提取<td>的逻辑会把嵌套表格的内容也提取出来,此时可以通过判断<td>的直接子节点过滤,或者调整XPath表达式仅匹配当前层级的单元格。
- 如果单元格内有换行、空格等冗余字符,使用
Trim()方法清理后再存储,保证数据整洁。 - 加载外部HTML时,注意处理编码问题,若文档编码不是UTF-8,需要在加载时指定正确的编码格式。
完整示例演示
以下是一个完整的示例,模拟解析一个包含id的表格,输出表头和所有行数据:
using HtmlAgilityPack;
using System;
using System.Collections.Generic;
namespace HtmlTableParseDemo
{
class Program
{
static void Main(string[] args)
{
// 模拟待解析的HTML内容,包含一个id为userTable的表格
string htmlContent = @"<html>
<body>
<table id='userTable'>
<tr>
<th>用户ID</th>
<th>用户姓名</th>
<th>用户年龄</th>
</tr>
<tr>
<td>1001</td>
<td>张三</td>
<td>25</td>
</tr>
<tr>
<td>1002</td>
<td>李四</td>
<td>28</td>
</tr>
</table>
</body>
</html>";
HtmlDocument htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(htmlContent);
// 定位id为userTable的表格
HtmlNode targetTable = htmlDoc.DocumentNode.SelectSingleNode("//table[@id='userTable']");
if (targetTable == null)
{
Console.WriteLine("未找到目标表格");
return;
}
// 提取表头
List<string> headers = new List<string>();
HtmlNode firstRow = targetTable.SelectSingleNode(".//tr[1]");
if (firstRow != null)
{
HtmlNodeCollection headerCells = firstRow.SelectNodes(".//th");
if (headerCells != null)
{
foreach (HtmlNode cell in headerCells)
{
headers.Add(cell.InnerText.Trim());
}
}
}
Console.WriteLine("表头:" + string.Join(", ", headers));
// 提取表格数据
List<List<string>> tableData = new List<List<string>>();
HtmlNodeCollection rows = targetTable.SelectNodes(".//tr");
if (rows != null)
{
for (int i = 1; i < rows.Count; i++)
{
HtmlNode row = rows[i];
List<string> rowData = new List<string>();
HtmlNodeCollection cells = row.SelectNodes(".//td");
if (cells != null)
{
foreach (HtmlNode cell in cells)
{
rowData.Add(cell.InnerText.Trim());
}
}
tableData.Add(rowData);
}
}
// 输出数据
Console.WriteLine("表格数据:");
foreach (List<string> row in tableData)
{
Console.WriteLine(string.Join(" | ", row));
}
}
}
}
运行上述代码后,会先输出表头内容,再逐行输出表格中的数据,实现精确解析指定表格的需求。开发者可以根据实际HTML文档的结构,调整定位表达式和数据提取逻辑,适配不同的表格解析场景。
HtmlAgilityPackHTML解析表格提取Csharp修改时间:2026-06-14 13:21:34