你可能也遇到过这种场景:想用 C# 网页爬虫 把某个网站的关键信息抓下来,落到 CSV 或数据库里;结果刚写完 HttpClient,就开始 403、超时、数据时有时无。
这篇就用一个真实可落地的 Web Scraping 流程,把“请求拿到 HTML”和“解析提取字段”拆开讲清楚:ScraperAPI 负责更稳定地拿页面,HtmlAgilityPack 负责把 HTML 解析成你要的数据。
做数据抓取时,最折磨人的往往不是解析,而是“你根本拿不到稳定的页面内容”。常见原因包括:
IP/频率限制:同一出口请求一多就被拦
反爬策略:验证码、JS 校验、WAF 等导致返回内容不完整
网络波动:偶发超时,重试机制没写好就直接丢数据
页面差异:同一个 URL,不同地区/设备返回结构不一致
如果你不想把时间都花在维护代理池、处理封禁和重试上,可以把“获取页面”这件事外包给专门的抓取接口。
👉 用 ScraperAPI 把 C# 爬虫的请求稳定性拉起来
思路很简单:
ScraperAPI:你把目标 URL 交给它,它负责尽量返回可用的 HTML 响应(更省心、更稳定)
HtmlAgilityPack:拿到 HTML 后,用 XPath/节点遍历提取字段(更快、更可控)
这也是很多生产环境里更常见的分工:抓取链路稳定 + 解析逻辑清晰,整体维护成本会更低。
项目类型建议用 .NET Console App,方便先跑通流程再封装成服务/定时任务。
安装依赖(任选一种方式):
Package Manager Console:
Install-Package ScraperApi
Install-Package HtmlAgilityPack
dotnet CLI:
dotnet add package ScraperApi
dotnet add package HtmlAgilityPack
如果你准备长期维护这个 C# 网页爬虫,建议顺手把“请求层”先标准化(比如统一超时、重试、日志)。
👉 看看 ScraperAPI 在 .NET 里怎么接入更顺手
下面用一个示例站点(比如 coinmarketcap.com 这类展示列表数据的页面)演示完整链路:抓 HTML → 解析表格行 → 导出 CSV。你也可以把最后一步换成写数据库。
把 API Key 放在环境变量里更安全,比如 SCRAPERAPI_KEY,避免写进代码仓库。
csharp
using ScraperApi;
using System.Net;
static async Task GetHtmlAsync()
{
var apiKey = Environment.GetEnvironmentVariable("SCRAPERAPI_KEY");
if (string.IsNullOrWhiteSpace(apiKey))
throw new InvalidOperationException("Missing env var: SCRAPERAPI_KEY");
HttpClient client = ScraperApiClient.GetProxyHttpClient(apiKey);
client.BaseAddress = new Uri("https://coinmarketcap.com");
var response = await client.GetAsync("/");
if (response.StatusCode != HttpStatusCode.OK)
throw new HttpRequestException($"Request failed: {(int)response.StatusCode}");
return await response.Content.ReadAsStringAsync();
}
核心思路:找到 tbody,遍历每行 tr,再取列 td。实际页面结构可能会变,所以一定要做空值和列数检查。
csharp
using HtmlAgilityPack;
using System.Text;
static Dictionary<string, string> ParseHtml(string html)
{
var result = new Dictionary<string, string>();
var doc = new HtmlDocument();
doc.LoadHtml(html);
var tableBody = doc.DocumentNode.SelectSingleNode("//tbody");
var rows = tableBody?.SelectNodes("tr");
if (rows == null) return result;
foreach (var row in rows)
{
var cols = row.SelectNodes("td");
if (cols == null || cols.Count < 4) continue;
var name = HtmlEntity.DeEntitize(cols[2].InnerText).Trim();
var price = HtmlEntity.DeEntitize(cols[3].InnerText).Trim();
if (!string.IsNullOrWhiteSpace(name) && !result.ContainsKey(name))
result.Add(name, price);
}
return result;
}
别把文件写死到个人路径,直接输出到当前目录更通用。
csharp
static void WriteToCsv(Dictionary<string, string> data, string filePath = "webscraping.csv")
{
var sb = new StringBuilder();
sb.AppendLine("Name,Price");
foreach (var item in data)
sb.AppendLine($"{item.Key},\"{item.Value}\"");
File.WriteAllText(filePath, sb.ToString(), Encoding.UTF8);
}
csharp
static async Task Main(string[] args)
{
var html = await GetHtmlAsync();
var data = ParseHtml(html);
WriteToCsv(data);
}
跑通后你会得到一个 CSV,里面是“名称 + 价格”两列。后续要加字段也很直接:在解析时多取几列或更精确的 XPath 即可。
页面是 JS 渲染:你抓到的可能是“空壳 HTML”,需要开启渲染或换数据源
XPath 很脆:站点改版就全挂,建议把关键节点选择写成可回退的策略
请求频率控制:别一上来就并发拉满,先加延时/重试/退避策略
数据清洗:InnerText 经常带空格和换行,记得 Trim(),必要时做标准化
当你把这个 Web Scraping 流程放进定时任务或生产服务里,“稳定拿到 HTML”基本决定了你后面的解析能不能正常发挥。
👉 用 ScraperAPI 把抓取链路做成更省维护的 C# 网页爬虫
把 CSV 输出换成数据库写入(比如批量 upsert)
增加重试与日志,定位偶发失败的 URL
把解析逻辑按字段拆函数,站点改版时更容易维护
如果你希望我把示例升级成“可并发抓多个页面 + 自动重试 + 可配置导出(CSV/DB)”的版本,也可以把你的目标站点结构或字段清单发我。