使用 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
.
也没有
您面临的下一个问题是 SelectSingleNode
和 SelectNodes
方法 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>
,然后再进一步解析它们。
可能有一些方法可以更改解析器以不同方式检测 td
和 tr
,通过在加载文档之前更改节点的 ElementFlags 来使用,但我的尝试都遇到了同样的问题正如你已经遇到的那样。
HtmlNode.ElementsFlags.Remove("td");
HtmlNode.ElementsFlags.Add("td", HtmlElementFlag.Closed | HtmlElementFlag.Empty);
HtmlNode.ElementsFlags.Remove("tr");
HtmlNode.ElementsFlags.Add("tr", HtmlElementFlag.Closed);
我有一个由 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
.
您面临的下一个问题是 SelectSingleNode
和 SelectNodes
方法 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 anycallspan=33
orwidth=171
for example.
你最好的行动可能是回到报告的来源并直接查询数据库。或者先关闭任何空的 <td>
,然后再进一步解析它们。
可能有一些方法可以更改解析器以不同方式检测 td
和 tr
,通过在加载文档之前更改节点的 ElementFlags 来使用,但我的尝试都遇到了同样的问题正如你已经遇到的那样。
HtmlNode.ElementsFlags.Remove("td");
HtmlNode.ElementsFlags.Add("td", HtmlElementFlag.Closed | HtmlElementFlag.Empty);
HtmlNode.ElementsFlags.Remove("tr");
HtmlNode.ElementsFlags.Add("tr", HtmlElementFlag.Closed);