项目

开始使用 AngleSharp 库

要求

AngleSharp 目前提供两种版本:针对 Windows 的.NET 4.6.2 及以上版本,以及一般适用于.NET Standard 2.0 平台。

尽管大部分库功能不需要.NET 4.6.2,但你可以创建自己的 fork 并修改它以支持之前的.NET Framework 版本。

通过 NuGet 获取 AngleSharp

将 AngleSharp 集成到项目中最简单的方法是使用 NuGet。你可以在包管理控制台(PM)中打开并输入以下命令:

Install-Package AngleSharp

或者也可以使用图形化的库包管理器("解决方案的 NuGet 包管理器")。在官方 NuGet 在线仓库中搜索 "AngleSharp" 可以找到这个库。

第一步

在最简单的情况下,你已经有了一个文档源并希望解析它。这可能看起来像这样:

using System;
using AngleSharp;
using AngleSharp.Html.Parser;

class MyClass {
    static async Task Main() {
        // 使用AngleSharp的默认配置
        IConfiguration config = Configuration.Default;

        // 创建一个使用给定配置评估网页的上下文
        IBrowsingContext context = BrowsingContext.New(config);

        // 待解析的源
        var source = "<h1>Some example source</h1><p>This is a paragraph element";

        // 创建虚拟请求以指定要加载的文档(这里从固定字符串加载)
        IDocument document = await context.OpenAsync(req => req.Content(source));

        // 对文档做些操作,如下面所示
        Console.WriteLine("序列化原始文档:");
        Console.WriteLine(document.DocumentElement.OuterHtml);

        IElement p = document.CreateElement("p");
        p.TextContent = "This is another paragraph.";

        Console.WriteLine("在body中插入另一个元素...");
        document.Body.AppendChild(p);

        Console.WriteLine("再次序列化文档:");
        Console.WriteLine(document.DocumentElement.OuterHtml);
    }
}

当然,你还可以继续进行更多 DOM 操作。

浏览上下文

IBrowsingContext 表示一个用于执行文档评估的浏览上下文。这是解析任何 HTML 页面所必需的构造。它还允许提交表单、跟随链接、下载资源等。我们可以将其想象为标准浏览器中的一个标签页。

另一种方式是在开始时使用以下代码:

IBrowsingContext context = BrowsingContext.New(config);
IHtmlParser parser = context.GetService<IHtmlParser>();
var source = "<h1>Some example source</h1><p>This is a paragraph element";
IDocument document = parser.ParseDocument(source);

解析器

那么 IHtmlParser 是什么呢?这是一个类,代表 HTML5 解析器的前端。它有方法来创建 IHtmlDocument 实例,该实例承载解析后的 DOM。由于 HTML 对可能的错误相当宽容,没有像异常那样的东西。我们可能只会收到一些错误消息。这些消息可以通过特殊接口接收,应该被视为警告。

探索网络

AngleSharp 背后的理念是提供最先进的解析器(用于 CSS、HTML 和相关对象,如 URL),它们生成与现代浏览器相同的 DOM。相同的 DOM 意味着使用的是众所周知的 JavaScript /当前浏览器的 API。这个 API 被标准化,并为 Web 开发者所熟知。此外,DOM 交互的活力不再仅限于 JavaScript 或浏览器托管场景。AngleSharp 将使你能够在代码中基本实现现代浏览器的核心。

DOM

整个 DOM 已经被转移到逻辑类结构中。这部分结构的一个部分可能如下图所示。请注意,图片显示的是较旧的 DOM 模型。AngleSharp 当前版本实现了稍有不同的最新 DOM 模型。尽管如此,这张图仍然有助于获得正确的想法。

dom模型

DOM 有一些限制:

  • 通常不能随意创建元素,IDocument 通常有工厂实例,或者如 IHtmlDocument 这样的特殊化。
  • 已知元素的继承不可行。
  • 修改 DOM 必须遵循给定的路径。

这意味着你不能像写(注意:HtmlParagraphElement 元素是私有的,无论如何——它是 IHtmlParagraphElement 接口的默认实现):

var paragraph = new HTMLParagraphElement();

因为这在 JavaScript 中也不可行。你需要的是 IDocument 接口的实例。如果我们假设这个实例称为document,现在我们可以写:

IElement paragraph = document.CreateElement("p");

这会创建段落元素 <p> 并将其所属的文档赋值给节点。就像在 JavaScript/DOM 世界中一样,我们没有将段落添加到文档中的任何地方。如果元素未附加,它没有父节点,因此不会出现在 DOM 树中。因此,它不会被重新序列化,某些特殊操作也会失去意义。此外,对 DOM 树的查询不会显示给定的元素。

另一方面,这些限制导致所有构造函数标记为 internal 。这阻止了继承(即使类可能不是 sealed 的),并且要求用户遵循给定的路径。

脚本语言(如 JavaScript)的优点是,尽管 CreateElement 只返回一个 IElement ,但如果可用,用户仍然能够访问 IHtmlElement 等特殊属性和方法。在静态类型语言(如 C#)中,我们需要进行类型转换。一种出路是使用 dynamic ,或者像这样方便的扩展方法:

IHtmlParagraphElement paragraph = document.CreateElement<IHtmlParagraphElement>();

这确实可行,直接返回 IHtmlParagraphElement 类型的对象。无需进行类型转换,并且节省了一个可疑的字符串分配。这个特定的扩展方法位于 AngleSharp.Dom 命名空间中。

DOM 之外

AngleSharp 提供了许多无法通过标准化 DOM 属性和方法访问的属性和方法。为了区分标准化和扩展,添加了一个名为 DomNameAttribute 的简单属性类。当装饰的类/事件/方法或属性也在 W3C 官方规范中指定时,会在这些情况下应用此属性。此外,还设置了官方名称,因为 AngleSharp 遵循驼峰命名法,而 DOM 遵循小驼峰命名法。

AngleSharp 还提供了不在 W3C 官方规范中列出的对象。有时这些类是 W3C 定义对象的特殊化(例如 MathElementElement 派生,尽管 Element 也在官方规范中指定,但 MathElement 不是),或者是 AngleSharp 生态系统的一部分。

IConfiguration 接口可用于配置 AngleSharp 的行为。可以从示例实现(称为 Configuration )派生,直接使用示例实现(如 new Configuration() ),或者从头开始通过实现 IConfiguration 自定义配置。

如果没有提供配置,AngleSharp 将使用默认配置。也可以设置默认配置,从而完全消除配置传输的要求。在大多数情况下,使用 Configuration.Default 是有意义的。请注意,Configuration 是不可变的,所有 IConfiguration 的扩展方法都不会尝试修改传递的对象。它们总是返回未修改的对象,或带有修改的新对象。

最后,AngleSharp 还提供了一些非常有用的扩展方法,试图模仿 JavaScript 中的 jQuery 提供的功能。通过使用 AngleSharp 命名空间,你可以访问如 HtmlCssAttrText 这样的方法。这些方法操作于 IEnumerable<IElement> ,就像现有的 IHtmlCollection 一样。目的是简单地轻松修改给定的 DOM。

// 使用AngleSharp.Html.Parser;
// 使用AngleSharp.Dom;
// 使用AngleSharp;

// 为托管文档创建一个新的浏览上下文
IBrowsingContext context = Browsing.New(Configuration.Default);

// 为以下源代码生成HTML DOM
IDocument document = await context.OpenAsync(req => req.Content("<ul><li>First element<li>Second element<li>third<li class=bla>Last"));

// 获取所有li元素并将test属性设置为"test";elements仍包含所有li元素
IHtmlCollection<IElement> elements = document.QuerySelectorAll("li").Attr("test", "test");

需要注意的是,应用 TextHtml 会对 DOM 产生影响。例如,如果我们将它应用于包含多个元素的列表,其中列表的一些元素包含相同的列表中的其他元素,结果列表仍将包含所有这些元素;然而,文档不会。

这种行为的原因很简单:应用 Html 将删除节点的所有子节点,并附加通过解析给定源获取的新子节点。同样,Text 将删除所有子节点,并附加具有给定文本内容的 IText 节点。

在本文档中