文章目录
前言
手动从网站复制数据?那是实习生该做的,但你没有实习生。好消息: C#可以自动化繁琐的工作。虽然Python主导了网页抓取的话题,但C#已经成长为一个真正的竞争者,拥有强大的库、类型安全性和在生产中真正重要的性能。让我们深入了解它。

什么是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的集成终端)并运行:
dotnet --version
您应该看到类似"10.0.100"的内容。如果出现错误,说明SDK不在系统PATH中,请参阅下面的解决方案。
常见问题
"dotnet"不是内部或外部命令
安装程序没有将.NET添加到您的PATH。首先重新启动终端,看看是否能解决问题。如果这不起作用:
- Windows: 在开始菜单中搜索环境变量,编辑PATH,并添加*C:\Program Files\dotnet*
- macOS/Linux: 将export PATH=" P A T H : 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的集成终端)并运行这些命令:
dotnet new console -n WebScraper
cd WebScraper
这会创建一个名为"WebScraper"的新文件夹,其中包含您开始编码所需的一切。-n标志命名您的项目,您可以随意命名它,但要确保它不是像"test-script-final-final-version2"这样的东西,这样您就会在六个月后记住它的作用。
创建后,您的项目结构将如下所示:
WebScraper/
├── Program.cs # 您的主入口点
├── WebScraper.csproj # 项目配置文件
├── obj/ # 中间构建文件(忽略这个)
└── bin/ # 编译的输出放在这里
选择正确的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,从您的项目目录运行:
dotnet add package HtmlAgilityPack
您将看到确认已添加包的输出。该命令下载HtmlAgilityPack并自动更新您的项目文件。
要验证安装,在VS Code中打开WebScraper.csproj文件。您应该看到一个新的部分,如下所示:
<ItemGroup>
<PackageReference Include="HtmlAgilityPack" Version="1.11.61" />
</ItemGroup>
添加CsvHelper用于CSV导出
如果您无法将抓取的数据导出到某个有用的地方,那么抓取数据就毫无意义。您可以手动编写CSV格式化逻辑,连接字符串、转义逗号、处理换行符,但为什么要浪费时间重新发明轮子呢?CsvHelper的存在就是为了解决这个问题。
CsvHelper是C#中CSV操作的事实标准。它自动处理编码、特定于文化的格式和边缘情况(如包含逗号或引号的字段)。您定义一个类,传递一个对象列表,它就会生成格式正确的CSV。没有意外,没有凌晨2点的bug,因为某人的公司名称中有逗号。
但您可能会问,为什么选择CSV?因为它是通用数据格式。Excel打开它,Google表格导入它,Pandas读取它,数据库摄取它。对于您的第一个抓取项目,CSV是阻力最小的路径。您不必处理JSON模式验证、数据库连接或API速率限制,只需任何人都能理解的行和列。
一旦您的抓取器工作,您总是可以将CSV换成JSON、SQL或您的管道需要的任何东西。但从简单开始。
在您的项目目录中运行:
dotnet add package CsvHelper
就是这样。CsvHelper现在与HtmlAgilityPack一起在您的项目中了。如果您想查看其他NuGet包或探索不同版本,请浏览官方NuGet画廊。
现在您已经有了抓取和导出的工具。是时候编写实际代码了。
使用HtmlAgilityPack构建静态网页抓取器
对于这个示例项目,让我们从quotes.toscrape.com抓取引用,这是一个专为此目的而设计的练习网站。该网站显示带有作者和标签的引用。HTML是服务器呈现的,这意味着所有内容在加载时已经在页面源代码中。非常适合HtmlAgilityPack。
使用HtmlWeb.Load()加载HTML
HtmlAgilityPack提供两种获取网页的方法: 同步和异步。对于大多数抓取任务,尤其是当您刚开始学习时,同步更简单。
同步加载会阻止您的程序,直到页面完全加载。打开Program.cs并编写以下代码:
using HtmlAgilityPack;
var web = new HtmlWeb();
var doc = web.Load("https://quotes.toscrape.com/");
Console.WriteLine("页面加载成功!");
Console.WriteLine($"标题: {doc.DocumentNode.SelectSingleNode("//title").InnerText}");
保存文件并在终端中使用此命令运行它:
dotnet run
您将在终端中看到打印的页面标题。这是一项简单的任务,但它确认库可以工作,并为进一步的抓取任务奠定了基础。
使用SelectNodes()和SelectSingleNode()的XPath
XPath是用于导航HTML/XML结构的查询语言。它就像文档的SQL,一开始有点神秘,但一旦您理解了语法,就会非常强大。
基本XPath模式:
// 选择所有匹配的元素
var quoteNodes = doc.DocumentNode.SelectNodes("//div[@class='quote']");
// 选择第一个匹配的元素
var firstQuote = doc.DocumentNode.SelectSingleNode("//div[@class='quote']");
"//“告诉应用程序在文档中的任何位置搜索。[@class=‘quote’]过滤具有该特定类属性的元素。要找到它们,您应该知道如何在浏览器中"检查元素”。
让我们提取实际数据:
using HtmlAgilityPack;
var web = new HtmlWeb();
var doc = web.Load("https://quotes.toscrape.com/");
// 选择所有引用容器
var quoteNodes = doc.DocumentNode.SelectNodes("//div[@class='quote']");
foreach (var quoteNode in quoteNodes)
{
// 使用相对XPath提取嵌套元素(以.开头)
var text = quoteNode.SelectSingleNode(".//span[@class='text']").InnerText;
var author = quoteNode.SelectSingleNode(".//small[@class='author']").InnerText;
Console.WriteLine($"引用: {text}");
Console.WriteLine($"作者: {author}");
Console.WriteLine("---");
}
脚本前往网站,通过定义的XPath找到所需的信息,并打印引用和作者名称。
使用HtmlEntity.DeEntitize()清理HTML实体
如果您运行了上面的代码,您可能注意到终端中的文本看起来有点奇怪:
引用: "I have not failed. I've just found 10,000 ways that won't work."
那些"'"是HTML实体,特殊字符的编码表示。浏览器会自动解码它们,但当您提取InnerText时,您会得到原始编码版本。
要解决此问题,您必须在输出之前解码它们:
using HtmlAgilityPack;
var web = new HtmlWeb();
var doc = web.Load("https://quotes.toscrape.com/");
var quoteNodes = doc.DocumentNode.SelectNodes("//div[@class='quote']");
foreach (var quoteNode in quoteNodes)
{
var text = quoteNode.SelectSingleNode(".//span[@class='text']").InnerText;
var author = quoteNode.SelectSingleNode(".//small[@class='author']").InnerText;
// 将HTML实体解码为可读文本
text = HtmlEntity.DeEntitize(text);
Console.WriteLine($"引用: {text}");
Console.WriteLine($"作者: {author}");
Console.WriteLine("---");
}
干净多了。在写入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二进制文件。在您的项目目录中运行这些命令:
dotnet add package Selenium.WebDriver
dotnet add package Selenium.WebDriver.ChromeDriver
在无头模式下启动Chrome
无头模式在没有可见窗口的情况下运行Chrome。没有GUI意味着更少的内存使用和更快的执行。这是在Program.cs中编写的基本脚本:
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
// 配置Chrome选项
var options = new ChromeOptions();
options.AddArgument("--headless"); // 无GUI运行
options.AddArgument("--disable-gpu"); // 禁用GPU加速(建议用于无头)
options.AddArgument("--no-sandbox"); // 绕过操作系统安全模型(在某些环境中需要)
// 使用这些选项启动Chrome
var driver = new ChromeDriver(options);
try
{
driver.Navigate().GoToUrl("https://quotes.toscrape.com/js/");
Console.WriteLine($"页面标题: {driver.Title}");
}
finally
{
driver.Quit(); // 总是关闭浏览器
}
使用以下命令运行:
dotnet run
您不会看到浏览器窗口打开,但您应该在终端中看到:
页面标题: Quotes to Scrape
如果您在运行脚本后遇到找不到驱动程序的问题,请检查chromedriver.exe(或chromedriver)是否存在于输出文件夹中。某些杀毒软件会标记它,如果需要请添加例外。
为什么无头很重要:
- 速度. 没有您永远看不到的UI元素的渲染开销
- 服务器环境. 许多CI/CD服务器没有显示器
- 资源效率. 运行多个抓取器时内存使用较低
如果您正在调试并想看看Selenium在做什么,只需删除–headless参数。Chrome将可见地打开,您可以观察它导航并与页面交互。
使用driver.FindElements()提取元素
一旦页面加载并执行JavaScript,您可以像使用HtmlAgilityPack一样提取数据,但使用Selenium的API。
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
var options = new ChromeOptions();
options.AddArgument("--headless");
options.AddArgument("--disable-gpu");
var driver = new ChromeDriver(options);
try
{
driver.Navigate().GoToUrl("https://quotes.toscrape.com/js/");
// 等待JavaScript加载内容(重要!)
Thread.Sleep(2000); // 简单等待
// 查找所有引用容器
var quoteElements = driver.FindElements(By.CssSelector("div.quote"));
Console.WriteLine($"找到{quoteElements.Count}条引用\n");
foreach (var quoteElement in quoteElements)
{
// 从嵌套元素中提取文本
var text = quoteElement.FindElement(By.CssSelector("span.text")).Text;
var author = quoteElement.FindElement(By.CssSelector("small.author")).Text;
// 提取标签(多个元素)
var tagElements = quoteElement.FindElements(By.CssSelector("a.tag"));
var tags = tagElements.Select(t => t.Text).ToList();
Console.WriteLine($"引用: {text}");
Console.WriteLine($"作者: {author}");
Console.WriteLine($"标签: {string.Join(", ", tags)}");
Console.WriteLine("---");
}
}
finally
{
driver.Quit(); // 清理浏览器进程
}
脚本启动浏览器,导航到页面,等待元素动态加载,然后提取引用、作者名称和标签,最后在终端中打印它们。
元素选择: CSS选择器vs XPath
在前面的示例中,脚本使用CSS选择器来查找内容。但是,这并不总是抓取页面的完美方式。虽然CSS选择器更快更干净,但XPath对于复杂的树遍历更灵活,特别是当您需要跳转CSS无法轻松到达的父节点或兄弟节点时。
好消息是,Selenium支持CSS选择器和XPath。根据偏好选择。
CSS选择器:
driver.FindElement(By.CssSelector("div.quote"));
driver.FindElement(By.CssSelector("span.text"));
driver.FindElement(By.CssSelector("a[href='/author/Albert-Einstein']"));
XPath选择器:
driver.FindElement(By.XPath("//div[@class='quote']"));
driver.FindElement(By.XPath("//span[@class='text']"));
driver.FindElement(By.XPath("//a[contains(@href, 'Einstein')]"));
提取属性
需要href、src或其他属性?使用GetAttribute():
var authorLink = quoteElement.FindElement(By.CssSelector("a"));
var authorUrl = authorLink.GetAttribute("href");
Console.WriteLine($"作者URL: {authorUrl}");
显式等待
代码中的Thread.Sleep(2000)是等待JavaScript执行的粗略方法。它有效,但浪费时间。Selenium提供显式等待,轮询直到元素出现:
using OpenQA.Selenium.Support.UI;
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.Until(d => d.FindElements(By.CssSelector("div.quote")).Count > 0);
这最多等待10秒,但一旦找到元素就继续。比盲目睡眠效率高得多。
导出和结构化抓取的数据
打印到控制台对测试很好,但真正的项目需要您可以分析、共享或导入数据库的结构化数据。专业方法: 定义模型类,填充集合,并导出到CSV。
我们将继续上一节的Selenium示例并添加适当的数据导出。无论您是使用HtmlAgilityPack还是Selenium进行抓取,此模式都有效,导出逻辑保持不变。
在C#中创建数据模型类
不要玩弄松散的字符串,创建一个代表您正在抓取的内容的类。对于我们的引用示例:
public class Quote
{
public string Text { get; set; }
public string Author { get; set; }
public string Tags { get; set; }
public string Url { get; set; }
}
类很重要,因为它们的强类型在编译时而不是运行时捕获错误。如果您拼错了属性名称,编译器会立即注意到。您还可以获得IntelliSense支持、重构工具以及数据结构的精确文档。
使用CsvWriter.WriteRecords()写入CSV
现在让我们修改Selenium抓取器以填充Quote对象列表并导出它们:
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using CsvHelper;
using System.Globalization;
public class Quote
{
public string Text { get; set; }
public string Author { get; set; }
public string Tags { get; set; }
public string Url { get; set; }
}
class Program
{
static void Main()
{
var options = new ChromeOptions();
options.AddArgument("--headless");
options.AddArgument("--disable-gpu");
var driver = new ChromeDriver(options);
var quotes = new List<Quote>();
try
{
driver.Navigate().GoToUrl("https://quotes.toscrape.com/js/");
Thread.Sleep(2000); // 等待JavaScript加载
var quoteElements = driver.FindElements(By.CssSelector("div.quote"));
foreach (var quoteElement in quoteElements)
{
var text = quoteElement.FindElement(By.CssSelector("span.text")).Text;
var author = quoteElement.FindElement(By.CssSelector("small.author")).Text;
var tagElements = quoteElement.FindElements(By.CssSelector("a.tag"));
var tags = string.Join(", ", tagElements.Select(t => t.Text));
var authorLink = quoteElement.FindElement(By.CssSelector("a"));
var url = authorLink.GetAttribute("href");
quotes.Add(new Quote
{
Text = text,
Author = author,
Tags = tags,
Url = url
});
}
Console.WriteLine($"抓取了{quotes.Count}条引用。正在写入CSV...");
// 写入CSV
using (var writer = new StreamWriter("quotes.csv"))
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
csv.WriteRecords(quotes);
}
Console.WriteLine("导出完成!检查quotes.csv");
}
finally
{
driver.Quit();
}
}
}
脚本执行以下操作:
- 将所有引用抓取到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时,特殊字符(é、ñ、中文)显示为乱码。这是修复方法:
using (var writer = new StreamWriter("quotes.csv", false, new UTF8Encoding(true)))
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
csv.WriteRecords(quotes);
}
new UTF8Encoding(true)参数添加BOM。现在Excel正确解释UTF-8字符。
总结
您已经完成了一个完整的抓取工作流程,证明了C#对于生产就绪的网络自动化是可靠的。通过添加异步抓取、代理和错误处理,您的设置可以轻松处理现实世界的规模。接下来: 征服更棘手的网站,让数据向您的代码低头。
常见问题解答
最流行的C#网页抓取库是什么?
HtmlAgilityPack是大多数C#网页抓取项目的首选库。它轻量、文档完善,并开箱即用地处理带有XPath支持的静态HTML解析。对于JavaScript密集型网站,Selenium和PuppeteerSharp是标准选择,尽管它们在资源上更重。
如何设置C#网页抓取环境?
从Microsoft的官方网站安装.NET SDK,然后获取带有C# Dev Kit扩展的Visual Studio Code。使用dotnet new console创建一个新的控制台项目,通过dotnet add package HtmlAgilityPack添加HtmlAgilityPack,您就可以开始抓取了。在任何操作系统上整个设置大约需要10分钟。
我应该使用哪个库来抓取JavaScript呈现的页面?
当数据在初始页面呈现后加载时使用Selenium或PuppeteerSharp。HtmlAgilityPack看不到动态加载的内容,因为它只解析初始HTML响应。Selenium经过更多实战考验,而如果您熟悉Node.js的Puppeteer,PuppeteerSharp提供更清晰的API。
如何在C#中将抓取的数据导出到CSV文件?
使用dotnet add package CsvHelper安装CsvHelper,为您的数据创建一个模型类,然后使用CsvWriter.WriteRecords()将您的列表转储到文件中。记住使用CultureInfo.InvariantCulture和带有BOM的UTF-8编码,如果您希望Excel在不破坏特殊字符的情况下打开它。
C#网页抓取的一些最佳实践是什么?
始终检查robots.txt并尊重速率限制,在请求之间添加延迟,这样您就不会猛击服务器。在HTTP调用和DOM选择周围使用try-catch块,以防网站结构在没有警告的情况下发生变化。考虑使用像Decodo网页抓取API这样的服务用于生产场景,在这些场景中,您需要代理、CAPTCHA处理和可靠性,而无需维护麻烦。

被折叠的 条评论
为什么被折叠?



