【问题标题】:Error while parsing html table into dataset将 html 表解析为数据集时出错
【发布时间】:2017-04-12 15:39:57
【问题描述】:

我正在尝试将 html 文件中的表格解析为数据集。我使用 vb.net 创建了一个 Windows 应用程序,我正在使用以下函数:

Public Function GetDataSet(ByVal strWebFilePath As String) As DataSet

    Dim html As String = System.IO.File.ReadAllText(strWebFilePath)

    html = WebUtility.HtmlDecode(html)

    Dim dsHtml As New DataSet
    Dim htmldoc As New HtmlAgilityPack.HtmlDocument
    htmldoc.LoadHtml(html)

    Dim tables = htmldoc.DocumentNode.SelectNodes("//table//tr") _
                    .GroupBy(Function(x) x.Ancestors("table").First())

    For i As Integer = 0 To tables.Count - 1
        Dim rows = tables(i).ToList()
        dsHtml.Tables.Add(String.Format("Table {0}", i))

        Dim headers = rows(0).Elements("th").Select(Function(x) x.InnerText.Trim).ToList()

        If headers.Count > 0 Then

            For Each Hr In headers
                dsHtml.Tables(i).Columns.Add(Hr)
            Next

            For j As Integer = 1 To rows.Count - 1
                Dim row = rows(j)
                Dim dr = row.Elements("td").Select(Function(x) x.InnerText.Trim).ToArray()
                dsHtml.Tables(i).Rows.Add(dr)
            Next

        Else

            headers = rows(0).Elements("td").Select(Function(x) x.InnerText.Trim).ToList()

            For ColumnIndex As Integer = 0 To headers.Count - 1
                dsHtml.Tables(i).Columns.Add("F" & ColumnIndex.ToString)
            Next

            For j As Integer = 0 To rows.Count - 1
                Dim row = rows(j)
                Dim dr = row.Elements("td").Select(Function(x) x.InnerText.Trim).ToArray()
                dsHtml.Tables(i).Rows.Add(dr)
            Next


        End If

    Next

    Return dsHtml
End Function

一切正常,直到我解析 html 表,它的第一行包含具有 colspan=2 的列(第一行被视为标题,即使它不包含 <th> )。所以它抛出了这个异常:

System.Data.dll 中出现“System.ArgumentException”类型的未处理异常 附加信息:输入数组长于此表中的列数。

考虑这个表格示例:

<table>
<tr><td colspan=2>Links</td></tr>
<tr><td>1</td><td>www.stackoverflow.com</td></tr>
<tr><td>2</td><td>www.sqlservercentral.com</td></tr>
<tr><td>3</td><td>www.dba.stackexchange.com/</td></tr>
</table>

有没有办法把第一行分成两列:

  • 第一个包含Links
  • 第二个包含自动生成的值(即:Col1

【问题讨论】:

    标签: html vb.net winforms html-table html-agility-pack


    【解决方案1】:

    您需要一些关于如何解决列跨度、行跨度、缺少单元格、额外单元格、单元格中的值不一致等问题的约定。一般来说,在不知道数据表结构的情况下,很难将 html 表解析为DataTable

    在这个答案中,我将重点关注标题上的列跨度。

    有什么问题?

    您在第一个 &lt;tr&gt; 中依赖 &lt;th&gt; 计数,并根据找到的 &lt;th&gt; 向数据表添加列。所以如果你有这样的表:

    <table>
        <tr>
            <th colspan="2">A</th>
            <th>B</th>
        </tr>
        <tr>
            <td>1</td>
            <td>11</td>
            <td>111</td>
        </tr>
    </table>
    

    然后您假设您有 2 列,并且在添加行时,由于每行中有 3 个元素,您会收到异常。

    我该如何解决这个问题?

    在这里,我决定将&lt;th colspan="n"&gt;C&lt;/th&gt; 转换为名称为C1C2、...、Cn 的n 列。

    Dim headers = rows(0).Elements("th").Select(Function(x) _
        New With
        {
            .Name = x.InnerText.Trim,
            .Count = If(x.Attributes("colspan") Is Nothing, _
                1, Integer.Parse(x.Attributes("colspan").Value))
        }).ToList()
    

    那么当向数据表添加列时:

    For Each Hr In headers
        For index = 1 To Hr.Count
            Dim postFix = If(Hr.Count > 1, index.ToString(), "")
            dsHtml.Tables(i).Columns.Add(Hr.Name & postFix)
        Next
    Next
    

    【讨论】:

    • 离题:我不确定这是否是个好主意,但您可以摆脱那些用于获取基于 thtd 的标头的冗余代码使用:Dim headers = rows(0).Elements("th").Union(rows(0).Elements("td")).Select(... 这样您就可以删除代码中的else 部分。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-28
    • 2014-05-19
    相关资源
    最近更新 更多