使用C# HttpClient获取动态生成CSV文件时遇403禁止访问问题求助
解决C# HttpClient访问动态生成CSV接口返回403 Forbidden的问题
问题分析
你遇到的403 Forbidden错误,本质是服务器识别出你的请求并非来自正规浏览器,因此拦截了非浏览器发起的请求。默认情况下,HttpClient发送的请求不会携带浏览器常用的请求头(比如User-Agent),这是很多网站反爬或基础验证的规则。
解决方案
1. 给HttpClient添加浏览器风格的请求头
最关键的是添加User-Agent模拟浏览器请求;同时补充其他浏览器常用请求头,比如Accept、Accept-Language等,进一步降低被拦截概率。
2. 优化请求逻辑,便于排查问题
直接使用GetStreamAsync会在内部自动调用EnsureSuccessStatusCode抛出异常,不利于排查问题。建议先获取完整响应消息,打印状态码和响应头,方便后续调试。
3. 规范异步代码写法
原代码用Task.Run+Wait处理异步任务容易造成线程阻塞,建议改用async Main和直接await异步方法,符合C#异步编程最佳实践。
修改后的完整代码
using System; using System.IO; using System.Net.Http; using System.Threading.Tasks; namespace HttpClientTest { class Program { private static HttpClient httpClient = new HttpClient(); // 静态构造函数初始化请求头 static Program() { // 模拟Chrome浏览器的User-Agent httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"); // 指定可接受的内容类型 httpClient.DefaultRequestHeaders.Accept.ParseAdd("text/csv,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"); // 指定语言偏好 httpClient.DefaultRequestHeaders.AcceptLanguage.ParseAdd("pl-PL,pl;q=0.8,en-US;q=0.5,en;q=0.3"); } // 改用async Main static async Task Main(string[] args) { await DownloadARiMRData(); } private static async Task DownloadARiMRData() { int startYear = 2020; int endYear = DateTime.Now.Year; for (int i = 0; i <= (endYear - startYear); i++) { string yearValue = (startYear + i).ToString(); string sourceURL = string.Format("https://rejestrupraw.arimr.gov.pl/krpbducsv?search=&rok={0}&woj=&pow=&gmi=&grupa=&uprawa=&pageNumber=1&pageSize=12", yearValue); string fileName = string.Format(@"Powierzchnie_upraw_w_gminach_{0}.csv", yearValue); string targetDirectory = @"D:\Users\ARiMR\"; try { await DownloadAndSave(sourceURL, targetDirectory, fileName); Console.WriteLine($"{yearValue}年数据下载完成"); } catch (Exception ex) { Console.WriteLine($"下载{yearValue}年数据失败: {ex.Message}"); } } } private static async Task DownloadAndSave(string sourceFile, string destinationFolder, string destinationFileName) { Stream fileStream = await GetFileStream(sourceFile); if (fileStream != Stream.Null) { await SaveStream(fileStream, destinationFolder, destinationFileName); } } private static async Task<Stream> GetFileStream(string fileUrl) { try { var response = await httpClient.GetAsync(fileUrl); // 打印状态码和响应头,方便排查问题 Console.WriteLine($"请求URL: {fileUrl}"); Console.WriteLine($"状态码: {response.StatusCode}"); foreach (var header in response.Headers) { Console.WriteLine($"{header.Key}: {string.Join(", ", header.Value)}"); } // 确保请求成功,否则抛出异常 response.EnsureSuccessStatusCode(); return await response.Content.ReadAsStreamAsync(); } catch (HttpRequestException ex) { Console.WriteLine($"请求错误: {ex.Message}"); return Stream.Null; } catch (Exception ex) { Console.WriteLine($"未知错误: {ex.Message}"); return Stream.Null; } } private static async Task SaveStream(Stream fileStream, string destinationFolder, string destinationFileName) { if (!Directory.Exists(destinationFolder)) Directory.CreateDirectory(destinationFolder); string path = Path.Combine(destinationFolder, destinationFileName); using (FileStream outputFileStream = new FileStream(path, FileMode.Create)) { await fileStream.CopyToAsync(outputFileStream); } } } }
额外调试建议
如果修改后仍然返回403,可以打开浏览器开发者工具(F12),切换到网络标签页,手动下载一次CSV,查看请求的所有头信息(比如Referer、Cookie等),然后将这些头全部添加到httpClient.DefaultRequestHeaders中,完全模拟浏览器的请求行为。
内容的提问来源于stack exchange,提问作者Paksos
相关产品推荐
相关产品推荐

