如何使用RestSharp的Async异步方法批量爬取多个URL并填充Data类的Html属性?
我来帮你搞定这个批量异步爬取的问题,直接上修改后的完整方案和关键细节解释:
核心修改思路
原代码只处理了列表第一个元素,我们需要遍历所有Data对象,为每个对象创建异步爬取任务,然后并行执行并等待所有任务完成,同时优化RestClient的使用方式提升性能,还要处理URL编码避免请求错误。
修改后的完整代码
public class Data { public int Id { get; set; } public string Url { get; set; } public string Html { get; set; } public static List<Data> Datas = new List<Data>(); } class Program { // 复用RestClient(RestSharp最佳实践,减少连接开销) private static readonly RestClient _restClient = new RestClient(); // 改用async Main(.NET Core 3.0+支持,更优雅的异步入口) static async Task Main(string[] args) { // 生成10条测试数据 for (int i = 0; i < 10; i++) { Data.Datas.Add(new Data { Id = i, Url = "https://www.httpbin.org", Html = null, }); } // 执行批量爬取 await DoScrapeAllAsync(); Console.WriteLine("✅ 所有爬取任务完成!"); // 可选:验证结果,打印前3条数据的HTML长度 foreach (var data in Data.Datas.Take(3)) { Console.WriteLine($"数据ID {data.Id} | HTML内容长度: {data.Html?.Length ?? 0}"); } Console.ReadLine(); } static async Task DoScrapeAllAsync() { // 为每个Data对象创建独立的爬取任务 var scrapeTasks = Data.Datas.Select(scrapeSingleData).ToList(); // 等待所有异步任务全部完成 await Task.WhenAll(scrapeTasks); } // 单独处理单个Data对象的爬取逻辑 static async Task scrapeSingleData(Data dataItem) { try { // 构建带ScraperAPI的请求URL,必须对目标URL编码避免格式错误 var targetUrl = $"{dataItem.Url}/ip"; var requestUrl = $"http://api.scraperapi.com/?api_key=c3df2_fake_4d5e&url={Uri.EscapeDataString(targetUrl)}&country_code=us"; var req = new RestRequest(requestUrl, Method.GET); var htmlContent = await _restClient.GetAsync<string>(req); // 将爬取结果赋值给对应Data对象的Html属性 dataItem.Html = htmlContent; Console.WriteLine($"✅ 完成爬取 数据ID {dataItem.Id}"); } catch (Exception ex) { // 可选:添加异常处理,避免单个请求失败导致全部任务终止 Console.WriteLine($"❌ 爬取数据ID {dataItem.Id} 失败: {ex.Message}"); } } }
关键细节说明
并行异步处理:
- 用
Data.Datas.Select(scrapeSingleData)为每个Data生成异步任务,再通过Task.WhenAll等待所有任务完成,所有请求并行执行,效率远高于逐个同步处理。
- 用
RestClient复用:
- RestClient内部维护了HTTP连接池,全局复用一个实例可以减少TCP连接的创建销毁开销,大幅提升性能,这是RestSharp官方推荐的最佳实践。
URL编码:
- 用
Uri.EscapeDataString对目标URL进行编码,确保URL中的特殊字符(比如&、=)不会破坏请求URL的结构,避免请求失败。
- 用
优雅的异步入口:
- .NET Core 3.0+支持
async Main,直接用await等待爬取任务,替代原代码的空循环等待,既优雅又避免CPU空转。如果是.NET Framework项目,可以把Main改成:static void Main(string[] args) { // ...生成测试数据 DoScrapeAllAsync().Wait(); // ...后续逻辑 }
- .NET Core 3.0+支持
可选的并发控制:
如果担心请求过于频繁被目标网站或ScraperAPI限制,可以用SemaphoreSlim限制并发数(比如最多5个同时请求),修改DoScrapeAllAsync方法即可:static async Task DoScrapeAllAsync() { // 限制最多5个并发请求 using var semaphore = new SemaphoreSlim(5); var scrapeTasks = Data.Datas.Select(async dataItem => { await semaphore.WaitAsync(); try { await scrapeSingleData(dataItem); } finally { semaphore.Release(); } }).ToList(); await Task.WhenAll(scrapeTasks); }
内容的提问来源于stack exchange,提问作者John Brk
相关产品推荐
相关产品推荐

