使用HtmlAgility包和 CssSelectors

分享于 

10分钟阅读

Web开发

  繁體 雙語

介绍

首先,我并不声称是XPath或者 正规表达式 专家,下面是我在解析客户机项目的HTML文档时所做的一些观察。

在以下示例中,我使用HtmlAgility包( HAP ) 将HTML加载到文档对象模型( DOM ) 中,并解析为节点。 另外,有些情况下,我不得不在不是真正节点的元素上解析文档,比如注释。

除了一般性的观察,我将指出 HAP.CSSSelectors 包提供的扩展方法,这些方法允许更容易地选择。

Background

我已经成功地使用HTMLAgility包为客户机,解析HTML文档提取相关信息。 CssSelector扩展将增加一个新级别的抽象级别来收集所需的数据。

使用代码

这个例子的包需要使用NuGet导入。 软件包描述将载入项目中,但是你需要设置NuGet软件包管理器来恢复库。

在项目中,我包含一个非常简单的html文件,其中包含了我在项目中需要解决的问题的示例。

要进行测试,你需要将HTML文件复制到下列驱动器和目录- C:testdata

HtmlAgility有许多类可以用于它,包括表示DOM的各个部分的类和枚举,这些类包括 HtmlAttribute。HtmlAttributeCollection。HtmlCommentNode等。

我们要检查的第一个班级是HtmlDocument班级。 此类具有加载文档并将文档解析为相应部分的方法。

在附加源代码中,我使用( 第X 部分)的命名来调用代码的每个部分,其中X 是一个数字。

要使用,需要实现以下行:

( 1 )


HtmlAgilityPack.agpack = new HtmlAgilityPack.HtmlDocument();



调用的下一个方法是加载文档的方法。 可以从字符串中加载:


agpack.LoadHtml(Html string) 


//or from a resource - 


agpack.Load(@"c:testdatatestdat.htm");



web浏览器一样,HAP对提供的Html也很宽容。 你可以查询错误,但它不会中断。

文件包含在第二个字体标记上缺少一个close标记 misplaced。 在浏览器中效果良好,但在HAP中不会出现错误,但可以检查。

( 第 2部分)


var errors = agpack.ParseErrors;



ParseErrors将返回一个集合和一个错误计数。 有趣的是,关闭字体选项卡没有引发错误。 但错误的</tr>。

加载文档后,用于搜索的两种主要方法如下:


SelectNodes(string XPath) // from the DocumentNode




GetElementbyId(string Id) // from the HtmlDocument



由于只有一个 ID,getElementById将返回单个节点,SelectNodes将返回一组节点,因为使用XPath可以匹配一个或者多个条目。

我的客户有一个应用程序,它将多个文件添加到一起,用一个开始和结束注释分隔每个文档。 下面是我如何处理这里文档的组成部分的方法。 我所包含的文件有一个用注释划分的部分,注释在表单中:


<!-- Start Table: 1234 --> HTML Body <!-- End Table -->



1234可以能表示我们需要处理的某些类型的帐户号。

( 第 3部分)

你可以使用以下命令获取注释:


var comment = agpack.DocumentNode.SelectNodes("//comment()[contains(., 'Start Table:')]");



从整个文档的("//") 中选择包含当前位置(。)的注释,单词开始 table。

由于这是注释,它没有子节点,内部文本只是注释本身的文本。 如果你想要做的是解析注释,可以在注释( 本案例中的帐号) 中确定一个值,但这并不真正有帮助。 为此,我回到 正规表达式 和分组。

( 第 4部分)


var html = Regex.Match(agpack.DocumentNode.InnerHtml,@"<!-- Start Table: d* -->(?<one>.*)<!-- End Table -->",RegexOptions.Singleline).Groups[1];



在 html.Value 中,我们有两个标签之间的文本。

通过在DOM中查找元素,第一个示例是使用getElementById查找节点。 有三个表,但只有两个ID分配给他们。 一个是 id="abc",另一个是 id=""

让我们从 id="abc"开始查看 table:

( 第 5部分)


var node = agpack.GetElementbyId("abc");



这将返回一个表示 table的节点。 InnerHtml将包含 <table> </table> 标记之间的所有文本。 它还包含一个表示 table的DOM结构的节点集合。

( 第 6部分)

获取行节点的一种方法是使用Linq来发现这些节点,例如:


var rownodes = node.ChildNodes.Where(w => w.OriginalName == "tr");



如果你检查计数,你会发现你有三行。 然而,实际上有四行,第一个包装在 <插槽中的> </,> 将不会被找到。

另一种方法是在节点上使用SelectNodes来发现 tr 元素。


rownodes = node.SelectNodes("tr");



但这也没有找到所有的行,只是找到它的直接子。

那 node.SelectNodes("/tr"); 怎么办。

关于 node.SelectNodes("//tr");的好消息是它找到了丢失的行以及文档中的所有行( 12 )。

经过少量挖掘后,我发现以下两个解决方案:


rownodes = node.SelectNodes(node.XPath + "//tr");



//or



// http://www.w3schools.com/xsl/xpath_axes.asp


rownodes = node.SelectNodes("descendant::tr");



这将返回全部四,这对我来说。 我认为我已经假设从当前节点开始 SelectNodes,"//tr" 会工作,但"//" 表示从文档的root 搜索。 但是第二个选项是作为当前选定节点的后代工作的。

同样,我们可以使用相同的程序找到 tr 元素的所有 td 元素。 注意对于 table 3,即使它们是 <tr> 和 <字体> 和 <span> 元素的子元素,我们也会返回十二个。

( 第 7部分)


node = null;


node = agpack.GetElementbyId("table3")


nodes = node.SelectNodes("descendant::td");



我们到 HAP.CssSelectors 上

它位于HtmlAgility包之上,实际上确保它是作为NuGet包的一部分安装的。

它允许你使用CSS选择器而不是XPath来选择元素。 例如:

( 第 8部分)


rownodes = agpack.QuerySelectorAll("#abc tr");



在这种情况下,我不需要从节点中查找,只从整个文档中选择它返回了预期的4行。


listTDNodes = agpack.QuerySelectorAll("#table3 td");



下面是在第二行中只获取 <td>的( 三)的示例。


listTDNodes = agpack.QuerySelectorAll("#table3 tr:nth-child(2) td");



这返回了十二个项目,四行为 3列。 注意,QuerySelectorAll方法返回为列表 <节点>,而不是节点集合。 如果你计划混合和匹配,这一点很重要。

除了选择 with ( # ) 以外,你还可以通过使用XPath来比查找属性更容易地选择 class(.),。


listTDNodes = agpack.QuerySelectorAll(".table");



返回第一个和第三个 table,它的类型为 table。

Points of Interest

总之,CssSelectors扩展是另一个很有用的工具,可以轻松地选择元素,而不需要深入到XPath或者。 我知道我会期待在我自己的工作中实现这些发现。

历史记录

保持在这里进行的任何更改或者改进的运行更新。


PAC  PACK  Using  
相关文章