Html Agility Pack (HAP) 教程:.NET中强大的HTML解析与Web抓取利器

在 .NET 开发中,尤其是进行 Web 抓取、网页分析或处理 HTML 内容时,我们经常需要解析和操作 HTML 文档。虽然 .NET Framework 和 .NET Core 提供了一些 XML 相关的类,但对于格式不规范的 HTML,它们往往显得力不从心。这时,开源的 Html Agility Pack (HAP) 就成为了我们的得力助手。

什么是 Html Agility Pack (HAP)?

Html Agility Pack (HAP) 是一个用于 .NET 平台的敏捷 HTML 解析器。它能够解析“真实世界”中各种格式混乱的 HTML 文档,并构建一个可读写的 DOM(文档对象模型)树。这意味着你可以像操作 XML 文档一样,使用 XPath 或 LINQ to XML 的语法来查询和操作 HTML 元素及其属性。

Html Agility Pack (HAP) 教程:.NET中强大的HTML解析与Web抓取利器

Html Agility Pack (HAP) GitHub地址:https://github.com/zzzprojects/html-agility-pack

为什么选择 Html Agility Pack (HAP)?

  • 容错性强: HAP 能够很好地处理格式不规范的 HTML,即使标签不闭合、属性缺失等情况也能进行解析。
  • 易于使用: 其 API 设计简洁直观,学习曲线平缓。
  • 强大的查询能力: 支持 XPath 和 LINQ to XML,可以灵活地定位和选取 HTML 元素。
  • 完整的 DOM 支持: 提供了丰富的 DOM 操作方法,方便修改和创建 HTML 结构。
  • 性能良好: 对于大多数 Web 抓取和 HTML 处理任务来说,HAP 的性能足够高效。
  • 开源且活跃: 作为一个成熟的开源项目,HAP 拥有庞大的用户社区和持续的维护。

Html Agility Pack (HAP) 的基本用法

要开始使用 HAP,你需要通过 NuGet 包管理器将其添加到你的 .NET 项目中:

Install-Package HtmlAgilityPack

接下来,我们来看一些基本的使用示例:

1. 加载 HTML 字符串

using HtmlAgilityPack;
using System;

public class Example
{
    public static void Main(string[] args)
    {
        var html = @"
            <!DOCTYPE html>
            <html>
            <head><title>我的示例页面</title></head>
            <body>
                <h1>欢迎使用 HAP!</h1>
                <div class='content'>
                    <p class='intro'>这是一个段落。</p>
                    <ul>
                        <li class='item' data-id='1'>列表项 1</li>
                        <li class='item' data-id='2'>列表项 2</li>
                    </ul>
                </div>
            </body>
            </html>";

        // 创建 HtmlDocument 对象
        var doc = new HtmlDocument();
        // 加载 HTML 字符串
        doc.LoadHtml(html);

        // 获取文档标题
        var titleNode = doc.DocumentNode.SelectSingleNode("//head/title");
        Console.WriteLine($"页面标题: {titleNode?.InnerText}"); // 输出: 页面标题: 我的示例页面

        // 使用 XPath 获取第一个 h1 元素
        var headingNode = doc.DocumentNode.SelectSingleNode("//h1");
        Console.WriteLine($"第一个 H1: {headingNode?.InnerText}"); // 输出: 第一个 H1: 欢迎使用 HAP!
    }
}

2. 从文件加载 HTML

using HtmlAgilityPack;
using System;
using System.IO;

public class Example
{
    public static void Main(string[] args)
    {
        var filePath = "example.html"; // 替换为你的 HTML 文件路径
        var doc = new HtmlDocument();
        doc.Load(filePath);

        var headingNode = doc.DocumentNode.SelectSingleNode("//h1");
        Console.WriteLine($"第一个 H1: {headingNode?.InnerText}");
    }
}

3. 从 URL 加载 HTML

using HtmlAgilityPack;
using System;
using System.Net.Http;
using System.Threading.Tasks;

public class Example
{
    public static async Task Main(string[] args)
    {
        var url = "https://www.example.com";
        using var httpClient = new HttpClient();
        var html = await httpClient.GetStringAsync(url);

        var doc = new HtmlDocument();
        doc.LoadHtml(html);

        var titleNode = doc.DocumentNode.SelectSingleNode("//title");
        Console.WriteLine($"页面标题: {titleNode?.InnerText}");
    }
}

4. 使用 XPath 查询节点

using HtmlAgilityPack;
using System;
using System.Linq;

public class Example
{
    public static void Main(string[] args)
    {
        var html = @"<ul><li class='item'>Item 1</li><li class='item'>Item 2</li></ul>";
        var doc = new HtmlDocument();
        doc.LoadHtml(html);

        // 获取所有 class 为 'item' 的 li 元素
        var itemNodes = doc.DocumentNode.SelectNodes("//li[@class='item']");
        if (itemNodes != null)
        {
            foreach (var node in itemNodes)
            {
                Console.WriteLine($"列表项内容: {node.InnerText}");
            }
            // 输出:
            // 列表项内容: Item 1
            // 列表项内容: Item 2
        }

        // 获取 class 为 'item' 的第二个 li 元素的 data-id 属性
        var secondItemDataId = doc.DocumentNode.SelectSingleNode("//li[@class='item'][2]")?.GetAttributeValue("data-id", "");
        Console.WriteLine($"第二个列表项的 Data ID: {secondItemDataId}"); // 输出: 第二个列表项的 Data ID: 2
    }
}

5. 使用 LINQ to XML 查询节点

using HtmlAgilityPack;
using System;
using System.Linq;
using System.Xml.Linq;

public class Example
{
    public static void Main(string[] args)
    {
        var html = @"<div class='container'><p class='text'>Text 1</p><p class='text'>Text 2</p></div>";
        var doc = new HtmlDocument();
        doc.LoadHtml(html);

        var containerNode = doc.DocumentNode.SelectSingleNode("//div[@class='container']");
        if (containerNode != null)
        {
            var textNodes = containerNode.Descendants("p").Where(n => n.GetAttributeValue("class", "") == "text");
            foreach (var node in textNodes)
            {
                Console.WriteLine($"段落内容 (LINQ): {node.InnerText}");
            }
            // 输出:
            // 段落内容 (LINQ): Text 1
            // 段落内容 (LINQ): Text 2
        }
    }
}

Web 抓取实战示例

假设我们需要从一个网页中抓取所有的链接和它们的文本:

using HtmlAgilityPack;
using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.Linq;

public class WebScraper
{
    public static async Task Main(string[] args)
    {
        var url = "https://www.example.com"; // 替换为你要抓取的网址
        using var httpClient = new HttpClient();
        var html = await httpClient.GetStringAsync(url);

        var doc = new HtmlDocument();
        doc.LoadHtml(html);

        var linkNodes = doc.DocumentNode.SelectNodes("//a");

        if (linkNodes != null && linkNodes.Any())
        {
            Console.WriteLine("抓取到的链接:");
            foreach (var link in linkNodes)
            {
                var href = link.GetAttributeValue("href", "");
                var text = link.InnerText.Trim();
                Console.WriteLine($"- Text: {text}, URL: {href}");
            }
        }
        else
        {
            Console.WriteLine("未找到任何链接。");
        }
    }
}

总结

Html Agility Pack (HAP) 是一款强大且灵活的 .NET 库,能够有效地解析和操作各种格式的 HTML 文档。其简单易用的 API 和强大的查询能力(XPath 和 LINQ to XML)使其成为 .NET 开发人员进行 Web 抓取、网页分析和 HTML 处理的首选工具之一。掌握 HAP 的使用,将极大地提高你在处理 HTML 相关任务时的效率和代码质量。

评论