110 lines
3.6 KiB
C#
110 lines
3.6 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Net;
|
||
using System.Net.Http;
|
||
using System.Threading.Tasks;
|
||
|
||
namespace Admin.NET.Core.Service;
|
||
|
||
public static class ProxyHelper
|
||
{
|
||
private static List<(string ip, int port)> _proxyList = new();
|
||
private static readonly Random _random = new();
|
||
|
||
/// <summary>
|
||
/// 初始化代理池并验证可用性
|
||
/// </summary>
|
||
/// <param name="filePath">CSV 文件路径,每行格式:ip,port</param>
|
||
/// <param name="testUrl">可用性验证的测试地址,建议用目标网站或者 https://httpbin.org/ip</param>
|
||
public static async Task LoadAndValidateProxiesAsync(string filePath, string testUrl = "https://httpbin.org/ip")
|
||
{
|
||
if (!File.Exists(filePath)) throw new FileNotFoundException("代理IP文件不存在", filePath);
|
||
|
||
var lines = File.ReadAllLines(filePath);
|
||
var tempList = new List<(string ip, int port)>();
|
||
|
||
foreach (var line in lines.Skip(1)) // 跳过表头
|
||
{
|
||
var parts = line.Split(',');
|
||
if (parts.Length >= 2 && int.TryParse(parts[1], out var port))
|
||
{
|
||
tempList.Add((parts[0], port));
|
||
}
|
||
}
|
||
|
||
// 异步验证代理是否可用
|
||
var tasks = tempList.Select(proxy => IsProxyAvailableAsync(proxy.ip, proxy.port, testUrl));
|
||
var results = await Task.WhenAll(tasks);
|
||
|
||
_proxyList = tempList.Where((proxy, index) => results[index]).ToList();
|
||
|
||
Console.WriteLine($"✅ 验证通过的代理数量:{_proxyList.Count}");
|
||
|
||
if (!_proxyList.Any())
|
||
throw new Exception("没有可用的代理IP,请检查代理池是否失效");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 验证代理是否可用
|
||
/// </summary>
|
||
private static async Task<bool> IsProxyAvailableAsync(string ip, int port, string testUrl)
|
||
{
|
||
try
|
||
{
|
||
var handler = new HttpClientHandler
|
||
{
|
||
Proxy = new WebProxy(ip, port),
|
||
UseProxy = true,
|
||
};
|
||
|
||
using var client = new HttpClient(handler);
|
||
client.Timeout = TimeSpan.FromSeconds(5);
|
||
|
||
var response = await client.GetAsync(testUrl);
|
||
return response.IsSuccessStatusCode;
|
||
}
|
||
catch
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 使用随机代理发起 GET 请求,并支持重试
|
||
/// </summary>
|
||
public static async Task<string> GetWithRandomProxyAsync(string url, int maxRetry = 5, int delayBetweenRetry = 1000)
|
||
{
|
||
if (!_proxyList.Any()) throw new Exception("代理列表为空,请先调用 LoadAndValidateProxiesAsync()");
|
||
|
||
for (int attempt = 0; attempt < maxRetry; attempt++)
|
||
{
|
||
var (ip, port) = _proxyList[_random.Next(_proxyList.Count)];
|
||
|
||
try
|
||
{
|
||
var handler = new HttpClientHandler
|
||
{
|
||
Proxy = new WebProxy(ip, port),
|
||
UseProxy = true,
|
||
};
|
||
|
||
using var client = new HttpClient(handler);
|
||
client.Timeout = TimeSpan.FromSeconds(10);
|
||
|
||
var response = await client.GetAsync(url);
|
||
response.EnsureSuccessStatusCode();
|
||
return await response.Content.ReadAsStringAsync();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"❌ 代理 {ip}:{port} 请求失败(第 {attempt + 1} 次): {ex.Message}");
|
||
await Task.Delay(delayBetweenRetry);
|
||
}
|
||
}
|
||
|
||
throw new Exception("多次尝试使用代理请求失败");
|
||
}
|
||
}
|