You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用HTML Agility Pack爬取时DataTable重复列名报错如何解决

问题修复与实现方案

核心错误原因

  • XPath查询语法错误:table.SelectNodes("//tr") 中的//会从整个HTML文档根节点查找所有tr元素,而非当前遍历到的table节点下的tr,导致所有表格的行被重复塞入同一个DataTable,多次读取不同表格的同名列表头,触发重复列名报错。
  • 缺少重名列兼容逻辑:就算修复XPath问题,单个表格内部如果出现重名列,依然会触发报错,直接跳过重名列会导致表头列数和数据行单元格数不匹配,后续赋值时触发“列不存在”报错。

修复后完整代码

using System.Data;
using System.IO;
using System.Text;
using System.Windows.Forms;
using HtmlAgilityPack;

public void WebDataScrap()
{
    try
    {
        const string url = "https://www.wsj.com/market-data/quotes/MY/XKLS/0146/financials/annual/cash-flow";
        var web = new HtmlWeb();
        var doc = web.Load(url);
        const string classValue = "cr_dataTable";
        var resultDataset = new DataSet();

        foreach (HtmlNode table in doc.DocumentNode.SelectNodes($"//table[@class='{classValue}']") ?? Enumerable.Empty<HtmlNode>())
        {
            // 给无Id的表格设置默认名称
            var tableName = string.IsNullOrEmpty(table.Id) ? $"Table_{resultDataset.Tables.Count + 1}" : table.Id;
            var resultTable = new DataTable(tableName);

            // 修复XPath,只查当前table下的tr
            foreach (HtmlNode row in table.SelectNodes(".//tr") ?? Enumerable.Empty<HtmlNode>())
            {
                var headerCells = row.SelectNodes("th");
                if (headerCells != null)
                {
                    foreach (HtmlNode cell in headerCells)
                    {
                        string colName = cell.InnerText.Trim();
                        // 处理重名列,自动加后缀
                        int suffix = 1;
                        string tempColName = colName;
                        while (resultTable.Columns.Contains(tempColName))
                        {
                            tempColName = $"{colName}_{suffix}";
                            suffix++;
                        }
                        resultTable.Columns.Add(tempColName);
                    }
                    continue; // 表头行不需要处理数据逻辑
                }

                var dataCells = row.SelectNodes("td") ?? Enumerable.Empty<HtmlNode>();
                if (dataCells.Any())
                {
                    var dataRow = resultTable.NewRow();
                    // 兼容列数和单元格数不一致的情况
                    int cellCount = Math.Min(dataCells.Count, resultTable.Columns.Count);
                    for (int i = 0; i < cellCount; i++)
                    {
                        dataRow[i] = dataCells[i].InnerText.Trim();
                    }
                    resultTable.Rows.Add(dataRow);
                }
            }
            resultDataset.Tables.Add(resultTable);
        }

        // 导出到CSV,每个表格对应一个CSV文件,也可以合并导出到Excel的不同Sheet
        ExportDatasetToCsv(resultDataset, @"C:\你的导出路径\WSJ财务数据");
        MessageBox.Show("数据爬取导出完成");
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.ToString());
    }
}

// 无需第三方依赖的CSV导出方法
private void ExportDatasetToCsv(DataSet ds, string exportPathPrefix)
{
    if (!Directory.Exists(Path.GetDirectoryName(exportPathPrefix)))
    {
        Directory.CreateDirectory(Path.GetDirectoryName(exportPathPrefix));
    }
    foreach (DataTable table in ds.Tables)
    {
        var filePath = $"{exportPathPrefix}_{table.TableName}.csv";
        var sb = new StringBuilder();
        // 写入表头
        IEnumerable<string> columnNames = table.Columns.Cast<DataColumn>().Select(c => c.ColumnName);
        sb.AppendLine(string.Join(",", columnNames));
        // 写入数据行
        foreach (DataRow row in table.Rows)
        {
            IEnumerable<string> fields = row.ItemArray.Select(field => 
                $"\"{field.ToString().Replace("\"", "\"\"")}\""); // 处理内容里的逗号和引号
            sb.AppendLine(string.Join(",", fields));
        }
        File.WriteAllText(filePath, sb.ToString(), Encoding.UTF8);
    }
}

可选优化

如果需要导出为单个Excel文件,每个表格对应一个Sheet,可以引入EPPlus或NPOI库实现,逻辑和导出CSV类似,遍历DataSet的Table对应生成Excel的Sheet即可。

内容的提问来源于stack exchange,提问作者Marcus Tan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 21:45:05