返回博客

使用 C# 进行网页抓取

Maryia Stsiopkina

2022-03-212 min read

网页抓取是通过自动化方式检索数据的过程。它在许多场景中必不可少,例如用于竞争对手价格监控、房地产报价信息、营销线索生成、情绪监控、新闻文章或财务数据聚合等。 

在编写网络抓取代码时,首先要决定使用什么编程语言。您可以使用多种语言来编写,例如 Python、JavaScript、Java、Ruby 或 C#。以上语言都提供强大的网页抓取功能。 

在本文中,我们将探索 C# 并为您展示如何创建真实的 C# 公共网络爬虫。请记住,即使我们使用 C#,您也可以将此信息调整为 .NET 平台支持的所有语言,包括 VB.NET 和 F#。 

C# 网页抓取工具

在编写代码之前,第一步是选择合适的 C# 库或包。这些 C# 库或包将具有下载 HTML 页面、解析这些页面并从这些页面中提取所需数据的功能。最常见的 C# 包有以下这些:

  • ScrapySharp

  • Puppeteer Sharp

  • Html Agility Pack 

Html Agility Pack 是最常用的 C# 包,仅 Nuget 就有近 5000 万次下载。它之所以如此受欢迎,背后有多种原因,最重要的一个是这个 HTML 解析器能够直接或使用浏览器下载网页。这个软件包可以接受格式错误的 HTML 并支持 XPath。而且,它还可以解析本地 HTML 文件; 因此,我们在本文中也会使用这个包。 

ScrapySharp 为 C# 编程添加了更多功能。这个包支持 CSS 选择器并且可以模拟 web 浏览器。虽然 ScrapySharp 这一 C# 包被公认功能强大,但并没有程序员对它进行积极维护。 

作为知名 Node.js Puppeteer 项目 .NET 端口的 Puppeteer Sharp,使用相同的 Chromium 浏览器来加载页面。这个包也采用了 async-await 风格的代码,支持异步的、基于 Promise 的行为。如果您熟悉这个 C# 包并且需要浏览器来渲染页面,Puppeteer Sharp 可能是个不错的选择。

如何制作 C# 网络爬虫

如前文所述,现在我们将演示如何使用 Html Agility Pack 来编写 C# 公共网络抓取代码。我们将使用带有 Visual Studio Code 的 .NET 5 SDK。此代码已用 .NET Core 3 和 .NET 5 进行了测试,它应该可以与其他版本的 .NET 一起使用。

我们将按照以下假设场景来编写:抓取一家书店并收集书名和价格。  我们在编写 C# 网络爬虫之前先来设置开发环境。 

设置开发环境

对于 C# 开发环境,需要安装 Visual Studio Code。注意,如果您分别用 Visual Studio 和 Visual Studio Code 来编写 C# 代码,会得出两个完全不同的应用程序。 

安装 Visual Studio Code 后,安装 .NET 5.0 或更新版本。您也可以使用 .NET Core 3.1。安装完成后,打开终端并运行以下命令以验证 .NET CLI 或命令行界面是否正常工作:

dotnet --version

这时应该输出已安装的 .NET 版本号。

项目结构和依赖项关系

代码是构成 .NET 项目的一部分。为简单起见,创建一个控制台应用程序。然后,创建一个文件夹,您将在其中编写 C# 代码。打开终端并前往该文件夹​​。现在,输入以下命令:

dotnet new console

输入此命令后,会输出控制台应用程序已成功创建的确认信息。 

现在该安装所需的软件包了。要使用 C# 抓取网页,Html Agility Pack 是个不错的选择。可以输入以下命令来安装这个软件包:

dotnet add package HtmlAgilityPack

再安装一个包,这样我们就可以轻松地将抓取的数据导出到 CSV 文件:

dotnet add package CsvHelper

如果您使用的是 Visual Studio 而不是 Visual Studio Code,请单击“File”(文件),选择 “New Solution”(新建解决方案),然后点按 “Console Application”(控制台应用程序)。要安装依赖项,请按照下列步骤操作:

  • 选择“Project”(项目);

  • 单击“Manage Project Dependencies”(管理项目依赖项);此时会打开 NuGet 包窗口;

  • 搜索并选择 HtmlAgilityPack ; 

  • 最后,搜索并选择 CsvHelper,然后单击“Add Packages”(添加软件包)。

到了这里,第一步的代码就完成了。下一步是解析文档。 

解析 HTML:获取图书链接

在这部分代码中,我们将从网页中提取所需的信息在这个阶段,文档现在是 HtmlDocument 类型的对象。这个类可让两个函数来选择相关元素。这两个函数都接受 XPath 作为输入并返回 HtmlNode 或 HtmlNodeCollection。以下是这两个函数的签名:

public HtmlNodeCollection SelectNodes(string xpath);
public HtmlNode SelectSingleNode(string xpath);

我们先来讨论 SelectNodes。

对于这个 C# 网页抓取工具的例子,我们将从这个页面中抓取所有书籍的详细信息。首先,需要对它进行解析,以便提取到书籍的所有链接。因此,请在浏览器中打开此页面,右键单击书籍链接,然后单击 “Inspect”(检查)。这时将打开开发人员工具。 

了解标记后,您要选择的 Xpath 应该是这样的:

//h3/a

现在可以将此 Xpath 传递给SelectNodes函数。

HtmlDocument doc = GetDocument(url);
HtmlNodeCollection linkNodes = doc.DocumentNode.SelectNodes("//h3/a");

注意,SelectNodes 函数的 DocumentNode 属性是由 HtmlDocument 调用的。

变量 linkNodes 是一个集合。我们写一个 foreach 循环,逐个从链接中获取 href。我们需要解决一个小问题,页面上的链接是相对链接。因此,需要将它们转换为绝对 URL,才能抓取这些提取的链接。 

要转换这些相对 URL,我们可以使用 Uri 类。我们可以使用这个构造函数来获得一个具有绝对 URL 的 Uri 对象。

Uri(Uri baseUri, string? relativeUri);

有了 Uri 对象,我们只要检查 AbsoluteUri 属性就可以获取完整的 URL。

我们可以把所有这些写在一个函数中,以保持代码的条理性。

static List<string> GetBookLinks(string url)
    {
        var bookLinks = new List<string>();
        HtmlDocument doc = GetDocument(url);
        HtmlNodeCollection linkNodes = doc.DocumentNode.SelectNodes("//h3/a");
        var baseUri = new Uri(url);
        foreach (var link in linkNodes)
        {
            string href = link.Attributes["href"].Value;
            bookLinks.Add(new Uri(baseUri, href).AbsoluteUri);
        }
        return bookLinks;
    }

 在这个函数中,我们从一个空的 List<string> 对象开始。在 foreach 循环中,我们将所有链接添加到该对象中并返回到它。

现在该修改 Main() 函数了,这样我们就可以测试到目前为止我们所写的 C# 代码。将函数修改成以下这样:

static void Main(string[] args)
{
    var bookLinks = GetBookLinks("http://books.toscrape.com/catalogue/category/books/mystery_3/index.html");
    Console.WriteLine("Found {0} links", bookLinks.Count);
}

要运行这段代码,打开终端并前往包含此文件的目录,然后输入以下内容:

dotnet run

输出结果应该是这样:

Found 20 links

让我们进入下一个部分,我们将处理所有的链接以获取相关图书数据。

解析 HTML:获取书籍详细信息

此时,我们已有一个包含书籍 URL 的字符串列表。我们只要写一个循环,用我们已经写好的 GetDocument 函数就可以首先获取文档。然后,我们将使用 SelectSingleNode 函数来提取书名和价格。

为了保持数据的条理性,让我们从一个类开始。这个类将代表一本书。这个类将有两个属性:标题 和 价格。显示如下:

public class Book
{
    public string Title { get; set; }
    public string Price { get; set; }
} 

现在,在浏览器中打开一个图书页面,为 Title – //h1 创建 XPath。为价格创建 Xpath 有点难,因为底部的其他书籍也应用了同样的类。

价格的 Xpath 应显示如下:

//div[contains(@class,"product_main")]/p[@class="price_color"]

注意,XPath 包含双引号。我们必须在这些字符前加一个反斜杠来转义。 

现在我们可以使用 SelectSingleNode 函数来获取节点,然后使用 InnerText 属性来获取元素中包含的文本。我们可以将所有内容放在一个函数中,如下所示:

static List<Book> GetBookDetails(List<string> urls)
{
    var books = new List<Book>();
    foreach (var url in urls)
    {
        HtmlDocument document = GetDocument(url);
        var titleXPath = "//h1";
        var priceXPath = "//div[contains(@class,\"product_main\")]/p[@class=\"price_color\"]";
        var book = new Book();
        book.Title = document.DocumentNode.SelectSingleNode(titleXPath).InnerText;
        book.Price = document.DocumentNode.SelectSingleNode(priceXPath).InnerText;
        books.Add(book);
    }
    return books;
}

这个函数将返回一个 书籍 对象列表。现在该更新 Main() 函数了。

static void Main(string[] args)
{
    var bookLinks = GetBookLinks("http://books.toscrape.com/catalogue/category/books/mystery_3/index.html");
    Console.WriteLine("Found {0} links", bookLinks.Count);
    var books = GetBookDetails(bookLinks);
}

这个网络抓取程序的最后一部分是以 CSV 格式导出数据。

导出数据

如果您还没有安装 CsvHelper,可以通过在终端中运行命令 dotnet add package CsvHelper 来完成此操作。

导出功能非常简单。首先,我们需要创建一个 StreamWriter 并将 CSV 文件名作为参数发送。接下来,我们将使用这个对象来创建一个 CsvWriter。最后,我们使用 WriteRecords 函数,只需一行代码就可以编写所有书籍。 

为了确保所有资源都正确关闭,我们可以使用 using 块。我们也可以用一个函数概括所有内容,如下所示:

static void exportToCSV(List<Book> books)
{
    using (var writer = new StreamWriter("./books.csv"))
    using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
    {
        csv.WriteRecords(books);
    }
}

最后,我们可以从 Main() 函数中调用这个函数:

static void Main(string[] args)
{
    var bookLinks = GetBookLinks("http://books.toscrape.com/catalogue/category/books/mystery_3/index.html");
    var books = GetBookDetails(bookLinks);
    exportToCSV(books);
}

就是这么简单!要运行这段代码,打开终端并运行以下命令:

dotnet run

几秒钟之内,您就可以创建一个 books.csv 文件。  

总结

如果您想写个 C# 爬虫,您可以使用多个软件包。在这篇文章中,我们展示了如何运用 Html Agility Pack,这是一款强大而易用的软件包。这是一个可以进一步增强的简单示例;例如可以尝试在这段代码中加入上述逻辑来处理多个页面。 

更多的技术教程,一定要去看看像用 R 进行网络抓取或者机器学习的网络抓取这样的博文。您也可以了解我们自己的网络爬虫,这些工具适用于大多数网站。

关于作者

Maryia Stsiopkina

文案

Maryia Stsiopkina 在 Oxylabs 担任一名初级文案。随着她对写作的热情逐渐发展,她在不同的时间点上不是写令人毛骨悚然的侦探故事,就是写儿童童话故事。最终,她发现自己进入了科技仙境,拥有无数隐藏的领域值得他去探索。在业余时间,她用望远镜观鸟(有些人误以为是跟踪,这就是为什么 Maryia 有时会发现自己处于尴尬的境地),制作花卉饰品,并品尝很多泡菜和绿橄榄。

Oxylabs博客上的所有信息均按“原样”提供,仅供参考。对于您使用Oxylabs博客中包含的任何信息或其中可能链接的任何第三方网站中包含的任何信息,我们不作任何陈述,亦不承担任何责任。在从事任何类型的抓取活动之前,请咨询您的法律顾问,并仔细阅读特定网站的服务条款或取得抓取许可。

在这篇文章


  • C# 网页抓取工具

  • 如何制作 C# 网络爬虫

  • 总结

选择Oxylabs®,业务更上一层楼


隐私政策

oxylabs.cn© 2024 保留所有权利©