C#网页抓取指南: 从零到生产代码(2025版)
手动从网站复制数据?那是实习生该做的,但你没有实习生。好消息: C#可以自动化繁琐的工作。虽然Python主导了网页抓取的话题,但C#已经成长为一个真正的竞争者,拥有强大的库、类型安全性和在生产中真正重要的性能。让我们深入了解它。
Zilvinas Tamulis
12月 12日, 2025年
15 分钟阅读

什么是C#网页抓取?
网页抓取是从网站自动提取数据,可以把它想象成批量下载公开可见但令人烦恼地困在HTML中的信息。开发人员将其用于价格监控、潜在客户生成、市场研究、竞争对手分析,以及基本上任何手动复制粘贴会让你发疯的场景。
C#并不总是抓取的明显选择。Python凭借Beautiful Soup和Scrapy占据了这个领域。但.NET 8改变了游戏规则,提供了跨平台支持、改进的性能和成熟的生态系统。出现了各种优雅处理静态HTML解析的库,而Selenium和PuppeteerSharp则处理JavaScript密集型网站。结果如何?C#现在提供类型安全、异步功能和IDE工具,使抓取感觉不像是将脚本粘在一起,而更像是真正的工程。
在本指南中,您将从头开始构建一个功能齐全的C#抓取器。我们要讨论环境设置、依赖管理、从静态和动态页面提取数据,以及将所有内容导出到干净的CSV文件。
先决条件: 您应该了解基本的C#语法并理解面向对象编程的概念。如果您可以编写一个类并理解方法的作用,那就没问题。这适用于Windows、Linux和macOS,.NET不歧视。
设置您的C#网页抓取环境
在抓取任何内容之前,您需要三样东西: .NET SDK(实际的编译器和运行时)、Visual Studio Code(您的IDE)和NuGet包管理器(安装库)。
以下是它们如何协同工作的: .NET SDK将您的C#代码编译成可执行程序并提供dotnet CLI工具。Visual Studio Code只是一个具有超能力的文本编辑器,语法高亮、调试、IntelliSense,但它实际上不编译任何东西。从技术上讲,您可以在记事本中编写C#并使用SDK编译它,但为什么要折磨自己呢?最后,NuGet允许您轻松地将第三方库添加到您的工作中,这样您就不必从头开始发明HTTP请求。
专业提示: 使用VS Code的集成终端。它将所有内容保持在一个窗口中,您不会失去对哪个终端属于哪个项目的跟踪。
安装.NET SDK和Visual Studio Code
让我们完成设置,这样您就可以开始编写代码了。
步骤1. 下载.NET SDK
前往Microsoft的.NET下载页面并获取最新的.NET SDK(8.0或更新版本)。运行安装程序,点击几次下一步,让它完成。这也会安装NuGet,所以您不必担心单独的安装,可以立即使用它。
步骤2. 下载Visual Studio Code
为您的操作系统获取Visual Studio Code。安装并启动应用程序。打开后,按Ctrl+Shift+X(macOS上为Cmd+Shift+X)打开扩展面板。
步骤3. 安装C#扩展
搜索并安装这些:
- C# Dev Kit(Microsoft的官方扩展包)
- C# Extensions(确保它不是已弃用的版本)

这些为您提供IntelliSense、调试和语法高亮。它们是高效编写和测试代码的必需品。
步骤4. 验证安装
打开终端(或VS Code的集成终端)并运行:
一键获取完整项目代码
您应该看到类似"10.0.100"的内容。如果出现错误,说明SDK不在系统PATH中,请参阅下面的解决方案。
常见问题
"dotnet"不是内部或外部命令
安装程序没有将.NET添加到您的PATH。首先重新启动终端,看看是否能解决问题。如果这不起作用:
- Windows: 在开始菜单中搜索环境变量,编辑PATH,并添加*C:\Program Files\dotnet*
- macOS/Linux: 将export PATH=" PATH : PATH: PATH:HOME/.dotnet添加到您的.bashrc或.zshrc文件(位于您的主目录中。可以使用"cd /"命令找到),然后运行source ~/.bashrc
VS Code找不到SDK
打开VS Code设置(Ctrl+,/Cmd+,),搜索"dotnet path",并手动将其指向您的SDK安装目录。通常,在Windows上是C:\Program Files\dotnet\dotnet.exe,在macOS上是/usr/local/share/dotnet/dotnet。
使用dotnet new创建控制台项目
让我们开始构建项目。您首先将创建一个控制台应用程序,这是运行小型自动化任务和执行测试的最简单方法。
打开您的终端(或VS Code的集成终端)并运行这些命令:
这会创建一个名为"WebScraper"的新文件夹,其中包含您开始编码所需的一切。-n标志命名您的项目,您可以随意命名它,但要确保它不是像"test-script-final-final-version2"这样的东西,这样您就会在六个月后记住它的作用。
创建后,您的项目结构将如下所示:
选择正确的C#网页抓取库
C#没有单一的"官方"抓取库,因为不同的网站需要不同的方法。一些网站提供加载时已准备好解析的纯HTML。其他网站在初始页面加载后使用JavaScript框架来呈现内容。您需要适合工作的正确工具,否则您最终会抓取空div,想知道为什么什么都不起作用。
静态与动态内容: 有什么区别?
静态内容是在到达浏览器之前在服务器上完全呈现的HTML。当您查看页面源代码(Ctrl+U/Cmd+U)时,您会看到想要抓取的实际数据。新闻网站、博客和文档页面通常属于这一类。
动态内容是在页面加载后由JavaScript生成的。初始HTML通常是一个带有空容器的骨架,JavaScript使用AJAX请求或客户端呈现填充它们。单页应用程序(React、Vue、Angular)和现代电子商务网站因此而臭名昭著。如果您查看源代码但看不到您要找的数据,那就是动态的。
HtmlAgilityPack vs Selenium vs PuppeteerSharp
以下是您可以选择的领先C#网页抓取库:
库
最适合
优点
缺点
HtmlAgilityPack
静态HTML解析
轻量、快速、简单的API、XPath支持
无法处理JavaScript呈现的内容
Selenium
带有JavaScript的动态页面
完整的浏览器自动化、广泛使用、稳定
慢、资源密集、需要WebDriver管理
PuppeteerSharp
无头Chrome自动化
现代API、适合SPA、比Selenium快
学习曲线陡峭、生态系统不太成熟
当数据在"查看源代码"中可见时使用HtmlAgilityPack。它是最快的选项,不会启动浏览器。非常适合抓取博客、具有服务器端呈现的产品列表或2015年之前构建的任何网站。如果您来自Python的Beautiful Soup,这就是您的等价物。
当内容在页面呈现后加载时使用Selenium,想想无限滚动、延迟加载的图像或通过API调用获取的数据。它经过实战考验,文档广泛。是的,它比解析原始HTML慢,但它实际上在现代网站上有效。
如果您想要Selenium的功能和更清晰的API,请使用PuppeteerSharp。它是Google的Puppeteer库的C#移植版本。如果您已经熟悉无头Chrome工作流程或需要高级浏览器控制(如请求拦截),这是一个不错的选择。
在本指南的以下部分中,您将看到如何使用HtmlAgilityPack处理静态内容和Selenium处理动态内容。这并不意味着它们是最好的选择,因为存在许多其他库,如ScrapySharp,它们根据您的特定需求提供完全不同的功能。
通过NuGet安装HtmlAgilityPack
要安装HtmlAgilityPack,从您的项目目录运行:
您将看到确认已添加包的输出。该命令下载HtmlAgilityPack并自动更新您的项目文件。
要验证安装,在VS Code中打开WebScraper.csproj文件。您应该看到一个新的部分,如下所示:
添加CsvHelper用于CSV导出
如果您无法将抓取的数据导出到某个有用的地方,那么抓取数据就毫无意义。您可以手动编写CSV格式化逻辑,连接字符串、转义逗号、处理换行符,但为什么要浪费时间重新发明轮子呢?CsvHelper的存在就是为了解决这个问题。
CsvHelper是C#中CSV操作的事实标准。它自动处理编码、特定于文化的格式和边缘情况(如包含逗号或引号的字段)。您定义一个类,传递一个对象列表,它就会生成格式正确的CSV。没有意外,没有凌晨2点的bug,因为某人的公司名称中有逗号。
但您可能会问,为什么选择CSV?因为它是通用数据格式。Excel打开它,Google表格导入它,Pandas读取它,数据库摄取它。对于您的第一个抓取项目,CSV是阻力最小的路径。您不必处理JSON模式验证、数据库连接或API速率限制,只需任何人都能理解的行和列。
一旦您的抓取器工作,您总是可以将CSV换成JSON、SQL或您的管道需要的任何东西。但从简单开始。
在您的项目目录中运行:
就是这样。CsvHelper现在与HtmlAgilityPack一起在您的项目中了。如果您想查看其他NuGet包或探索不同版本,请浏览官方NuGet画廊。
现在您已经有了抓取和导出的工具。是时候编写实际代码了。
使用HtmlAgilityPack构建静态网页抓取器
对于这个示例项目,让我们从quotes.toscrape.com抓取引用,这是一个专为此目的而设计的练习网站。该网站显示带有作者和标签的引用。HTML是服务器呈现的,这意味着所有内容在加载时已经在页面源代码中。非常适合HtmlAgilityPack。
使用HtmlWeb.Load()加载HTML
HtmlAgilityPack提供两种获取网页的方法: 同步和异步。对于大多数抓取任务,尤其是当您刚开始学习时,同步更简单。
同步加载会阻止您的程序,直到页面完全加载。打开Program.cs并编写以下代码:
保存文件并在终端中使用此命令运行它:
您将在终端中看到打印的页面标题。这是一项简单的任务,但它确认库可以工作,并为进一步的抓取任务奠定了基础。
使用SelectNodes()和SelectSingleNode()的XPath
XPath是用于导航HTML/XML结构的查询语言。它就像文档的SQL,一开始有点神秘,但一旦您理解了语法,就会非常强大。
基本XPath模式:
"//“告诉应用程序在文档中的任何位置搜索。[@class=‘quote’]过滤具有该特定类属性的元素。要找到它们,您应该知道如何在浏览器中"检查元素”。
让我们提取实际数据:
脚本前往网站,通过定义的XPath找到所需的信息,并打印引用和作者名称。
使用HtmlEntity.DeEntitize()清理HTML实体
如果您运行了上面的代码,您可能注意到终端中的文本看起来有点奇怪:
那些"'I'"是HTML实体,特殊字符的编码表示。浏览器会自动解码它们,但当您提取InnerText时,您会得到原始编码版本。
要解决此问题,您必须在输出之前解码它们:
干净多了。在写入CSV或JSON之前,始终运行DeEntitize(),您的数据分析师会感谢您。
现在让我们解决更复杂的问题: JavaScript呈现的页面。
使用Selenium抓取JavaScript呈现的页面
HtmlAgilityPack工作得很完美,直到您遇到一个"查看源代码"只显示空div容器的网站。
这就是Selenium拯救您的地方。它不仅仅是一个抓取和解析库,它是一个浏览器自动化框架。Selenium启动一个实际的Chrome(或Firefox)实例,导航到页面,等待JavaScript执行,然后让您从完全呈现的DOM中提取数据。
Selenium如何工作: WebDriver架构
Selenium使用WebDriver协议来控制浏览器。把它想象成一个遥控器:
- 您的C#代码向WebDriver发送命令(例如"导航到此URL",“单击此按钮”)
- WebDriver将这些命令转换为特定于浏览器的指令
- Chrome(通过ChromeDriver)执行指令并发送回结果
- 您的代码接收数据并继续
这种往返使Selenium比纯HTTP请求慢,因为您正在驱动一个完整的浏览器。尽管如此,当页面严重依赖JavaScript生成内容时,真正的浏览器引擎通常是唯一实用的选择。
负责任的自动化
自动化浏览器可以比人类更快地发送请求,用1000个并发Selenium实例猛击服务器会让您的IP立即被禁止。在请求之间添加延迟(Thread.Sleep()或更好的方法,使用指数退避)。尊重robots.txt。如果网站明确阻止自动化,不要试图规避它,使用像Decodo网页抓取API这样正确处理速率限制和代理的服务。
另外,如果您正在尝试AI辅助的抓取工作流程,请查看我们的ChatGPT网页抓取指南。
现在让我们为quotes.toscrape.com/js构建一个抓取器,这是您之前抓取的网站的JavaScript呈现版本。
安装Selenium.WebDriver和ChromeDriver
您需要两个包: Selenium库本身和控制Chrome的ChromeDriver二进制文件。在您的项目目录中运行这些命令:
在无头模式下启动Chrome
无头模式在没有可见窗口的情况下运行Chrome。没有GUI意味着更少的内存使用和更快的执行。这是在Program.cs中编写的基本脚本:
使用以下命令运行:
您不会看到浏览器窗口打开,但您应该在终端中看到:
如果您在运行脚本后遇到找不到驱动程序的问题,请检查chromedriver.exe(或chromedriver)是否存在于输出文件夹中。某些杀毒软件会标记它,如果需要请添加例外。
为什么无头很重要:
- 速度. 没有您永远看不到的UI元素的渲染开销
- 服务器环境. 许多CI/CD服务器没有显示器
- 资源效率. 运行多个抓取器时内存使用较低
如果您正在调试并想看看Selenium在做什么,只需删除–headless参数。Chrome将可见地打开,您可以观察它导航并与页面交互。
使用driver.FindElements()提取元素
一旦页面加载并执行JavaScript,您可以像使用HtmlAgilityPack一样提取数据,但使用Selenium的API。
脚本启动浏览器,导航到页面,等待元素动态加载,然后提取引用、作者名称和标签,最后在终端中打印它们。
好消息是,Selenium支持CSS选择器和XPath。根据偏好选择。
CSS选择器:
XPath选择器:
提取属性
需要href、src或其他属性?使用GetAttribute():
显式等待
代码中的Thread.Sleep(2000)是等待JavaScript执行的粗略方法。它有效,但浪费时间。Selenium提供显式等待,轮询直到元素出现:
这最多等待10秒,但一旦找到元素就继续。比盲目睡眠效率高得多。
导出和结构化抓取的数据
打印到控制台对测试很好,但真正的项目需要您可以分析、共享或导入数据库的结构化数据。专业方法: 定义模型类,填充集合,并导出到CSV。
我们将继续上一节的Selenium示例并添加适当的数据导出。无论您是使用HtmlAgilityPack还是Selenium进行抓取,此模式都有效,导出逻辑保持不变。
在C#中创建数据模型类
不要玩弄松散的字符串,创建一个代表您正在抓取的内容的类。对于我们的引用示例:
类很重要,因为它们的强类型在编译时而不是运行时捕获错误。如果您拼错了属性名称,编译器会立即注意到。您还可以获得IntelliSense支持、重构工具以及数据结构的精确文档。
使用CsvWriter.WriteRecords()写入CSV
现在让我们修改Selenium抓取器以填充Quote对象列表并导出它们:
脚本执行以下操作:
- 将所有<Quote>抓取到List中,而不是立即打印
- StreamWriter创建CSV文件
- CsvHelper的CsvWriter自动处理格式化、转义和标题
- WriteRecords()一次调用序列化整个列表,无需循环,无需手动格式化
- using语句确保文件正确关闭,即使发生异常
使用dotnet run命令运行此程序。您将在项目目录中获得一个quotes.csv文件。
处理CultureInfo和UTF-8 BOM以实现Excel兼容性
注意到CultureInfo.InvariantCulture参数了吗?这确保了一致的数字和日期格式,无论您系统的区域设置如何。没有它,德语系统可能使用逗号表示小数,而美国系统使用句点。不变文化使一切保持标准化。
Excel UTF-8问题
Excel有一个怪癖: 它不识别UTF-8文件,除非它们以字节顺序标记(BOM)开头。没有BOM,当您在Excel中打开CSV时,特殊字符(é、ñ、中文)显示为乱码。这是修复方法:
new UTF8Encoding(true)参数添加BOM。现在Excel正确解释UTF-8字符。
总结
您已经完成了一个完整的抓取工作流程,证明了C#对于生产就绪的网络自动化是可靠的。通过添加异步抓取、代理和错误处理,您的设置可以轻松处理现实世界的规模。接下来: 征服更棘手的网站,让数据向您的代码低头。
关于作者

Zilvinas Tamulis
技术文案
作为一名拥有 4 年以上工作经验的技术作家,Žilvinas 将自己在多媒体和计算机设计方面的学习与创建用户手册、指南和技术文档方面的实际专业知识相结合。他的工作包括利用 JavaScript、PHP 和 Python 的实践经验,开发每天有数百人使用的网络项目。
通过 LinkedIn 与 Žilvinas 联系。
Decodo 博客上的所有信息均按原样提供,仅供参考。对于您使用 Decodo 博客上的任何信息或其中可能链接的任何第三方网站,我们不作任何陈述,也不承担任何责任。


