使用 HtnlAgilityPack 从 HTML 文件中解析

Parsing from HTML file using HtnlAgilityPack

我有一个由 Oracle Reports 生成的 HTML (DTD HTML 4.0 Transitional) 文件。

这是 HTML 文件的来源:

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD><META content="IE=5.0000" http-equiv="X-UA-Compatible">

<META http-equiv="Content-Type" content="text/html; charset=windows-1251">
<META name="GENERATOR" content="MSHTML 11.00.9600.17801"></HEAD>
<BODY dir="LTR" bgcolor="#ffffff"> <!-- Created by Oracle Reports  --> 
<TABLE width="960" border="0" cellspacing="0" cellpadding="0">
  <TBODY>
  <TR valign="top">
    <TD height="9">
    <TD width="71" rowspan="3" colspan="3"><FONT face="Courier New" 
      size="1"><B><TT>Date</TT></B></FONT><BR>
    <TD>
    <TD width="89" rowspan="3" colspan="3"><FONT face="Courier New" 
      size="1"><B><TT>Target Number</TT></B></FONT>   
    <TD>
    <TD width="143" rowspan="3" colspan="7"><FONT face="Courier New" 
      size="1"><B><TT>Description</TT></B></FONT>   
    <TD colspan="11">
    <TD width="101" rowspan="3" colspan="4"><FONT face="Courier New" 
      size="1"><B><TT>Transaction </TT></B></FONT><BR><FONT face="Courier New" size="1"><B><TT>Sum</TT></B></FONT><BR>
    <TD colspan="2">
    <TD width="89" rowspan="3"><FONT face="Courier New" 
      size="1"><B><TT>Fee</TT></B></FONT>   
    <TD>
    <TD width="113" rowspan="3" colspan="4"><FONT face="Courier New" 
      size="1"><B><TT>Sum</TT></B></FONT>   
    <TD>
    <TD width="137" rowspan="3" colspan="2"><FONT face="Courier New" 
      size="1"><B><TT>Device </TT></B></FONT><BR><FONT face="Courier New" size="1"><B><TT>Id</TT></B></FONT><BR>
    <TD>
  <TR valign="top">
    <TD height="9">
    <TD>
    <TD>
    <TD colspan="3">
    <TD width="40" colspan="5"><FONT face="Courier New" 
      size="1"><B><TT>Reference</TT></B></FONT>   
    <TD colspan="3">
    <TD colspan="2">
    <TD>
    <TD>
    <TD>
  <TR valign="top">
    <TD height="9">
    <TD>
    <TD>
    <TD colspan="11">
    <TD colspan="2">
    <TD>
    <TD>
    <TD>
  <TR valign="top">
    <TD height="9">
    <TD width="71" rowspan="2" colspan="3"><FONT face="Courier New" 
      size="1"><TT>03/09/2015</TT></FONT>   
    <TD>
    <TD width="89" rowspan="2" colspan="3"><FONT face="Courier New" 
      size="1"><TT>4405641418</TT></FONT>   
    <TD>
    <TD width="143" rowspan="2" colspan="7"><FONT face="Courier New" 
      size="1"><TT>WWW.EXAMPLE.COM</TT></FONT>   
    <TD>
    <TD width="71" rowspan="2" colspan="9"><FONT face="Courier New" 
      size="1"><TT>524601231313</TT></FONT>   
    <TD>
    <TD width="101" rowspan="2" colspan="4"><FONT face="Courier New" 
      size="1"><TT> 1 087,00</TT></FONT>   
    <TD colspan="2">
    <TD width="89" rowspan="2"><FONT face="Courier New" 
      size="1"><TT>-26,09</TT></FONT>   
    <TD>
    <TD width="113" rowspan="2" colspan="4"><FONT face="Courier New" 
      size="1"><TT> 1 060,91</TT></FONT>   
    <TD>
    <TD width="137" rowspan="2" colspan="2"><FONT face="Courier New" 
      size="1"><TT>11055700</TT></FONT>   
    <TD>
  <TR valign="top">
    <TD height="9">
    <TD>
    <TD>
    <TD>
    <TD>
    <TD colspan="2">
    <TD>
    <TD>
    <TD>
  <TR>
    <TD height="5" colspan="43">
  <TR valign="top">
    <TD height="9">
    <TD width="71" rowspan="2" colspan="3"><FONT face="Courier New" 
      size="1"><TT>03/09/2015</TT></FONT>   
    <TD>
    <TD width="89" rowspan="2" colspan="3"><FONT face="Courier New" 
      size="1"><TT>4405641418</TT></FONT>   
    <TD>
    <TD width="143" rowspan="2" colspan="7"><FONT face="Courier New" 
      size="1"><TT>WWW.EXAMPLE.COM</TT></FONT>   
    <TD>
    <TD width="71" rowspan="2" colspan="9"><FONT face="Courier New" 
      size="1"><TT>524601231313</TT></FONT>   
    <TD>
    <TD width="101" rowspan="2" colspan="4"><FONT face="Courier New" 
      size="1"><TT> 55,00</TT></FONT>   
    <TD colspan="2">
    <TD width="89" rowspan="2"><FONT face="Courier New" 
      size="1"><TT>-1,32</TT></FONT>   
    <TD>
    <TD width="113" rowspan="2" colspan="4"><FONT face="Courier New" 
      size="1"><TT> 53,68</TT></FONT>   
    <TD>
    <TD width="137" rowspan="2" colspan="2"><FONT face="Courier New" 
      size="1"><TT>11055700</TT></FONT>   
    <TD>


</BODY></HTML>

我需要使用 HTML 敏捷包将 HTML 解析为我的 C# 实体。我无法访问 TD 标签中的 TT 标签。

这是 C# 代码:

var tds = DocumentNode.SelectSingleNode("//body").SelectNodes("//tr[td[contains(@width,'71') and contains(@colspan,'3')]]").Descendants("tt");

如何访问 TT 标签?

如果只是你想要的TT标签。

HtmlNodeCollection tds = DocumentNode.SelectNodes("//body[@dir='LTR']//table//tbody//tr//td//tt");

应该给你所有的 TT 标签。 下次你能不能给出一个更短更具体的 HTML 文件。这个没有 Table 或 tbody.

的结尾标记

此外,我认为您必须将嵌套标签的选项设置为 true,否则它将忽略 td 和 tt 标签。

HtmlAgilityPack.HtmlDocument htmlDoc = new HtmlAgilityPack.HtmlDocument();
htmlDoc.OptionFixNestedTags=true;

我认为 Kent 说对了,您的文档有很多未关闭的 <td> 标签,这会导致解析时出现问题。我想即使是 oracle 也强制在 IE5 兼容模式下呈现它是有原因的。

在调试器中查看时,您会看到 HtmlAgilityPack 在文档末尾添加了大量关闭标记(在调试器中检查 doc.DocumentNode.OuterHtml):

</td></td></td></td></td></td></td></td></td></td></td></td></td></td></td>
</td></td></tr></td></tr></td></td></td></td></td></td></td></td></td></tr>
</td></td></td></td></td></td></td></td></td></td></td></td></td></td></td>
</td></td></tr></td></td></td></td></td></td></td></td></tr></td></td></td>
</td></td></td></td></td></td></td></tr></td></td></td></td></td></td></td>
</td></td></td></td></td></td></td></td></tr></tbody></table></body></html>

这些并没有在它们应该关闭的地方关闭...不幸的是,OptionFixNestedTags 默认情况下是打开的,它似乎不会影响解析器,因为它确实需要关闭这些标签。 OptionAutoCloseOnEnd = false.

也没有

您面临的下一个问题是 SelectSingleNodeSelectNodes 方法 return null,不是空集合,因此您的代码将开始抛出空引用当找不到任何东西时,异常就像疯狂一样(这可能是您的代码中的情况,至少在我的小测试项目中是这样)。如果您使用的是 C#6,您至少可以使用 ?. 来抢占异常,但这不会修复搜索代码。

然后你首先调用 SelectSingleNode("//body") 然后调用 .SelectNodes("//..."),第二次调用不应使用锚定在文档根目录的 //,而应使用 .// 锚定在正文标签下方。因为它是你也可以删除 SelectSingleNode("//body") 调用。

由于嵌套问题,Xpath 不会在 tr 的正下方找到任何 td,这似乎符合您的描述。这是因为就 Agility Pack 的协调性而言,您正在寻找的 td 是它之前的 td 的子项

这是阅读时的结构:

<TR valign="top">
    <TD height="9">
        <TD width="71" rowspan="3" colspan="3"><FONT face="Courier New" 
        size="1"><B><TT>Date</TT></B></FONT><BR>

            <TD></td>
        </td>
    </td>
</tr>

因此,为了找到您的 tt 标签,您必须求助于:

var tds = doc.DocumentNode.SelectNodes("//body//tr//td[@width=71 and @colspan=3]");

Note that I also simplified the attribute lookups, as contains will cause issues if there are any callspan=33 or width=171 for example.

你最好的行动可能是回到报告的来源并直接查询数据库。或者先关闭任何空的 <td>,然后再进一步解析它们。

可能有一些方法可以更改解析器以不同方式检测 tdtr,通过在加载文档之前更改节点的 ElementFlags 来使用,但我的尝试都遇到了同样的问题正如你已经遇到的那样。

HtmlNode.ElementsFlags.Remove("td");
HtmlNode.ElementsFlags.Add("td", HtmlElementFlag.Closed | HtmlElementFlag.Empty);
HtmlNode.ElementsFlags.Remove("tr");
HtmlNode.ElementsFlags.Add("tr", HtmlElementFlag.Closed);