ZetCode

C# 使用 AngleSharp 解析 HTML

最后修改于 2023 年 7 月 5 日

C# AngleSharp 教程展示了如何使用 AngleSharp 库在 C# 中解析 HTML。

该库还可以解析 SVG、MathML 或 XML。

文档对象模型 (DOM) 是一个标准的树结构,其中每个节点包含来自 XML 结构的组件之一。 元素节点和文本节点是两种最常见的节点类型。 使用 DOM 函数,我们可以创建节点、删除节点、更改节点内容以及遍历节点层次结构。

节点、元素和标签是同义词。

words.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Words</title>
</head>
<body>

<ul>
    <li>sky</li>
    <li>cup</li>
    <li>water</li>
    <li>cloud</li>
    <li>bear</li>
    <li>wolf</li>
</ul>

</body>
</html>

在一些例子中,我们使用这个 HTML 文件。

C# AngleSharp 解析 HTML 字符串

在第一个例子中,我们从字符串中读取一个 HTML 文档。

Program.cs
using AngleSharp;

var html = @"
<html>
<head>
<title>My Title</title>
</head>
<body>
<h1>Heading I</h1>
<p>Paragraph I</p>
<p>Paragraph II</p>
</body>
</html>";

var config = Configuration.Default;
using var context = BrowsingContext.New(config);
using var doc = await context.OpenAsync(req => req.Content(html));

Console.WriteLine(doc.Title);
Console.WriteLine(doc.Body.InnerHtml.Trim());
Console.WriteLine(doc.FirstChild.NodeName.ToLower());
Console.WriteLine(doc.LastChild.NodeName.ToLower());

我们打印文档的标题、正文以及第一个和最后一个子节点的名称。

var config = Configuration.Default;
using var context = BrowsingContext.New(config);

要设置 AngleSharp,我们定义一个 BrowsingContext,并将 Configuration 传递给它。

using var doc = await context.OpenAsync(req => req.Content(html));

文档使用 OpenAsync 加载。

Console.WriteLine(doc.Title);

使用 Title 属性,我们可以获取文档的标题。

Console.WriteLine(doc.Body.InnerHtml.Trim());

通过 Body 属性,我们可以获取文档正文的内部 HTML。

Console.WriteLine(doc.FirstChild.NodeName.ToLower());
Console.WriteLine(doc.LastChild.NodeName.ToLower());

我们获取文档的第一个和最后一个子节点的名称。

$ dotnet run
My Title
<h1>Heading I</h1>
<p>Paragraph I</p>
<p>Paragraph II</p>
html
html

C# AngleSharp QuerySelectorAll

QuerySelectorAll 方法返回文档中与指定的选择器组匹配的元素的列表。

Program.cs
using AngleSharp;
using AngleSharp.Dom;

var html = @"
<html>
<p>words</p>
<ul>
<li>sky</li>
<li>cup</li>
<li>rock</li>
<li>water</li>
</ul>
</html>";

using var context = BrowsingContext.New(Configuration.Default);
using var doc = await context.OpenAsync(req => req.Content(html));
var els = doc.QuerySelectorAll("li");

foreach (var e in els)
{
    Console.WriteLine(e.Text());
}

在这个例子中,我们获取所有的 li 标签。 使用 Text 方法,我们可以获取节点的内容文本。

$ dotnet run
sky
cup
rock
water

C# AngleSharp 解析 HTML 文件

在下面的例子中,我们从 HTML 文件加载文档。

Program.cs
using AngleSharp;

var html = File.ReadAllText(@"/home/janbodnar/Documents/words.html");

var config = Configuration.Default;
using var context = BrowsingContext.New(config);
using var doc = await context.OpenAsync(req => req.Content(html));

var lis = doc.QuerySelectorAll("li");

foreach (var li in lis)
{
    Console.WriteLine(li.InnerHtml);
}

我们使用 File.ReadAllText 将 HTML 文件读取到字符串中。 然后我们选择所有的 li 标签并打印它们的内部 HTML。 (在这种情况下,它等于 Text 方法。)

C# AngleSharp 修改文档

在下面的例子中,我们加载一个 HTML 文件并修改它。 修改后的文档被写入磁盘。

Program.cs
using AngleSharp;
using AngleSharp.Text;

var html = File.ReadAllText(@"/home/janbodnar/words.html");

var config = Configuration.Default;
using var context = BrowsingContext.New(config);
using var doc = await context.OpenAsync(req => req.Content(html));

var wli = doc.CreateElement("li");
wli.TextContent = "smile";

var ul = doc.QuerySelector("ul");
ul.FirstElementChild.Remove();
ul.AppendChild(wli);

var p = doc.CreateElement("p");
p.TextContent = $"New paragraph{Symbols.NoBreakSpace}";
doc.Body.AppendChild(p);

File.WriteAllText("/home/janbodnar/words2.html", doc.ToHtml());

在这个例子中,我们添加了两个元素并删除了一个元素。

var wli = doc.CreateElement("li");
wli.TextContent = "smile";

使用 CreateElement 创建一个新元素。 我们使用 TextContent 属性设置它的内容。

var ul = doc.QuerySelector("ul");
ul.FirstElementChild.Remove();
ul.AppendChild(wli);

我们找到 ul 元素,并使用 Remove 删除它的第一个子元素。 然后我们使用 AppendChild 添加新创建的元素。

var p = doc.CreateElement("p");
p.TextContent = $"New paragraph{Symbols.NoBreakSpace}";
doc.Body.AppendChild(p);

我们还在正文的末尾添加一个段落。

File.WriteAllText("/home/janbodnar/words2.html", doc.ToHtml());

修改后的文档使用 WriteAllText 保存到磁盘。

C# AngleSharp 解析 HTML 页面

在下一个例子中,我们从一个简单的网站加载一个 HTML 页面。

Program.cs
using AngleSharp;
using AngleSharp.Dom;

var config = Configuration.Default.WithDefaultLoader();
using var context = BrowsingContext.New(config);

var url = "http://webcode.me";

using var doc = await context.OpenAsync(url);
// var title = doc.QuerySelector("title").InnerHtml;
var title = doc.Title;

Console.WriteLine(title);

var pars = doc.QuerySelectorAll("p");

foreach (var par in pars)
{
    Console.WriteLine(par.Text().Trim());
}

从页面的 HTML 文档中,我们获取它的标题和两个段落的内容。

var config = Configuration.Default.WithDefaultLoader();

要启用从 HTML 页面读取,我们调用 WithDefaultLoader 方法。

$ dotnet run
My html page
Today is a beautiful day. We go swimming and fishing.
Hello there. How are you?

C# 异步解析 HTML 页面

在下一个例子中,我们异步解析几个 HTML 页面。

Program.cs
using AngleSharp;
using AngleSharp.Dom;

var urls = new[] {
    "http://webcode.me", "http://example.com",
    "http://httpbin.org", "https://google.com", 
    "https://rust-lang.net.cn/", "https://golang.ac.cn/",
    "https://fsharp.org/", "https://clojure.org/",
    "https://perl.net.cn/", "https://gnu.org"
};

var config = Configuration.Default.WithDefaultLoader();
using var context = BrowsingContext.New(config);

var docs = new List<Task<IDocument>>();

foreach (var url in urls)
{
    docs.Add(context.OpenAsync(url));
}

Task.WaitAll(docs.ToArray());

foreach (var t in docs)
{
    var res = t.Result;
    Console.WriteLine(res.Title);

    res.Dispose();
}

该示例异步加载几个网页并解析它们的标题。

$ dotnet run
My html page
Example Domain
httpbin.org
Google
Rust Programming Language
The Go Programming Language
F# Software Foundation
Clojure
The Perl Programming Language - www.perl.org
The GNU Operating System and the Free Software Movement

C# AngleSharp 搜索结果

在下一个例子中,我们向 Google 搜索引擎提交一个请求并处理结果。

Program.cs
using System.Text.RegularExpressions;
using AngleSharp;
using AngleSharp.Dom;
using AngleSharp.Html.Dom;

using var context = BrowsingContext.New(Configuration.Default.WithDefaultLoader());
using var doc = await context.OpenAsync("https://www.google.com/");
var form = doc.QuerySelector<IHtmlFormElement>("form[action='/search']");

var term = "F# language";

using var res = await form.SubmitAsync(new {q = term});
var links = res.QuerySelectorAll<IHtmlAnchorElement>("a");

foreach (var link in links)
{
    var val = link.Attributes["href"].Value;
    
    if (!val.Contains("google"))
    {
        var rx = new Regex(@"(/url\?q=)([^&]*)(\&)", RegexOptions.Compiled);
        var match = rx.Match(val);

        if (match.Success)
        {
            Console.WriteLine(match.Groups[2]);
        }
    }
}

该示例打印搜索引擎为给定的搜索词返回的所有链接。

var form = doc.QuerySelector<IHtmlFormElement>("form[action='/search']"); 

我们找到表单。

using var res = await form.SubmitAsync(new {q = term});

表单使用 SubmitAsync 提交。

var links = res.QuerySelectorAll<IHtmlAnchorElement>("a");

从结果中,我们获取所有的链接。

foreach (var link in links)
{
    var val = link.Attributes["href"].Value;
...

我们遍历链接并获取它们的 href 属性的内容,其中包含链接。

if (!val.Contains("google"))

我们想排除搜索引擎链接,例如指向条款、隐私和类似内容的链接。

var rx = new Regex(@"(/url\?q=)([^&]*)(\&)", RegexOptions.Compiled);

if (match.Success)
{
    Console.WriteLine(match.Groups[2]);
}

我们使用正则表达式来处理链接。 我们感兴趣的链接以 /url?q= 前缀开头。 搜索引擎在 & 后缀之后向链接附加一些参数。 我们将正则表达式表达式分成三个组,并选择第二个组。

$ dotnet run
https://fsharp.org/
https://fsharp.org/learn/
https://fsharp.org/specs/language-spec/
https://fsharp.org/about/
https://fsharp.org/use/windows/
https://docs.microsoft.com/en-us/dotnet/fsharp/what-is-fsharp
https://medium.com/skills-matter/what-is-the-most-underrated-programming-language-fa...
https://www.planetgeek.ch/2020/12/16/c-vs-f/
https://en.wikipedia.org/wiki/F_Sharp_(programming_language)
https://en.wikipedia.org/wiki/F_Sharp_(programming_language)
https://sk.wikipedia.org/wiki/F_Sharp
...

来源

AngleSharp Github 项目

在本文中,我们使用 AngleSharp 在 C# 中解析了 HTML 文档。

作者

我叫 Jan Bodnar,是一位充满热情的程序员,拥有丰富的编程经验。 自 2007 年以来,我一直撰写编程文章。 迄今为止,我已经撰写了 1,400 多篇文章和 8 本电子书。 我在编程教学方面拥有超过十年的经验。

列出所有 C# 教程