You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在C#汽车广告采集应用中配置多代理轮询访问的实现方案

给你的C#汽车广告采集工具添加代理轮询功能

嘿,MariuszKK!我看了你这段采集otomoto.pl汽车信息的代码,要实现交替使用代理避免被封禁的需求其实很 straightforward。下面我来一步步帮你改造代码:

核心思路

  1. 维护一个代理服务器列表,每次请求时按顺序轮切换代理
  2. 为每个代理配置HttpClientHandler,让HttpClient通过指定代理发送请求
  3. 合理管理HttpClient的生命周期,避免资源泄漏

改造后的完整代码

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using HtmlAgilityPack;

class Program
{
    static async Task Main(string[] args)
    {
        // 1. 定义你的代理服务器列表,格式:http://ip:port 或带认证的 http://user:pass@ip:port
        var proxies = new List<string>
        {
            "http://proxy1.example.com:8080",
            "http://proxy2.example.com:8080",
            "http://proxy3.example.com:8080"
            // 可以继续添加更多代理
        };

        if (proxies.Count == 0)
        {
            Console.WriteLine("请至少配置一个代理服务器!");
            return;
        }

        int currentProxyIndex = 0;

        for (int i = 1; i < 10; i++)
        {
            string url = $"https://www.otomoto.pl/osobowe/audi/a3/?page={i}";
            HttpResponseMessage response = null;
            string html = string.Empty;

            try
            {
                // 2. 获取当前轮询的代理,并更新索引(循环复用)
                string currentProxy = proxies[currentProxyIndex];
                currentProxyIndex = (currentProxyIndex + 1) % proxies.Count;

                // 3. 配置HttpClientHandler使用指定代理
                var handler = new HttpClientHandler();
                handler.Proxy = new WebProxy(currentProxy);
                
                // 如果代理需要用户名密码认证,取消下面注释并填写信息
                // handler.Proxy.Credentials = new NetworkCredential("your_proxy_username", "your_proxy_password");

                // 4. 使用带代理的Handler创建HttpClient(用using自动释放资源)
                using var httpClient = new HttpClient(handler);
                // 可选:添加请求头模拟浏览器,降低被识别为爬虫的概率
                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");

                Console.WriteLine($"正在使用代理 {currentProxy} 访问第 {i} 页...");
                response = await httpClient.GetAsync(url);
                response.EnsureSuccessStatusCode(); // 检查请求是否成功
                html = await response.Content.ReadAsStringAsync();
            }
            catch (Exception ex)
            {
                Console.WriteLine($"请求第 {i} 页失败:{ex.Message}");
                // 可选:如果当前代理失效,可以跳过并尝试下一个,或者重试
                continue;
            }

            // 5. 原有的HTML解析逻辑保持不变
            var htmlDocument = new HtmlDocument();
            htmlDocument.LoadHtml(html);

            var cars = htmlDocument.DocumentNode.Descendants("div")
                .Where(node => node.GetAttributeValue("class", "") == "offers list")
                .ToList();

            if (cars.Count == 0)
            {
                Console.WriteLine("未找到车辆列表元素");
                continue;
            }

            var carItems = cars[0].Descendants("article")
                .Where(node => node.GetAttributeValue("data-test", "").Contains("search-result-item"))
                .ToList();

            foreach (var carItem in carItems)
            {
                try
                {
                    var marka = carItem.Descendants("a")
                        .Where(node => node.GetAttributeValue("class", "") == "offer-title__link")
                        .First().InnerText.Trim('\n', ' ');
                    Console.WriteLine($"品牌型号:{marka}");

                    var year = carItem.Descendants("li")
                        .Where(node => node.GetAttributeValue("data-code", "") == "year")
                        .First().InnerText.Trim('\n', ' ');
                    Console.WriteLine("年份:" + year);

                    var mileage = carItem.Descendants("li")
                        .Where(node => node.GetAttributeValue("data-code", "") == "mileage")
                        .First().InnerText.Trim('\n', ' ');
                    Console.WriteLine("里程:" + mileage);

                    try
                    {
                        var engine = carItem.Descendants("li")
                            .Where(node => node.GetAttributeValue("data-code", "") == "engine_capacity")
                            .First().InnerText.Trim('\n', ' ');
                        Console.WriteLine("发动机排量:" + engine);
                    }
                    catch
                    {
                        Console.WriteLine("发动机排量:无信息");
                    }

                    var fuel = carItem.Descendants("li")
                        .Where(node => node.GetAttributeValue("data-code", "").Contains("fuel_type"))
                        .First().InnerText.Trim('\n', ' ');
                    Console.WriteLine("燃油类型:" + fuel);
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"解析车辆信息失败:{ex.Message}");
                }
                Console.WriteLine("---------------------------------------------------");
            }

            // 可选:添加延迟,避免请求过于频繁触发反爬
            await Task.Delay(1000);
        }
    }
}

关键改进点说明

  • 代理轮询:通过currentProxyIndex计数器循环复用代理列表,实现交替使用不同代理
  • 资源管理:用using关键字自动释放HttpClient和HttpClientHandler,避免资源泄漏
  • 异常处理:添加了请求和解析过程的异常捕获,避免单个请求失败导致整个程序崩溃
  • 反爬优化:添加了浏览器UA头和请求延迟,降低被网站识别为爬虫的概率

额外建议

  1. 确保代理服务器的可用性,定期清理失效代理
  2. 如果代理需要认证,记得正确配置NetworkCredential
  3. 可以考虑添加重试机制:当某个代理请求失败时,自动切换到下一个代理重试当前页面
  4. 不要一次性请求太多页面,合理控制请求频率

内容的提问来源于stack exchange,提问作者MariuszKK

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 09:12:25