diff --git a/core/douyu/proxy.py b/core/douyu/proxy.py index 16d1f9d..254e9e7 100644 --- a/core/douyu/proxy.py +++ b/core/douyu/proxy.py @@ -16,7 +16,8 @@ class ProxyManager: def get_proxy(self) -> Optional[str]: """ - 从代理API获取代理IP + 从代理API获取代理IP,支持API返回多个IP(多行格式), + 逐一验证返回第一个可用的。 Returns: 代理URL,格式: http://ip:port @@ -29,17 +30,29 @@ class ProxyManager: text = response.text.strip() logger.debug(f"代理API响应: {text}") - # 解析IP:Port格式 - match = re.search(r'(\d+\.\d+\.\d+\.\d+):(\d+)', text) - if match: - ip = match.group(1) - port = match.group(2) - proxy = f"http://{ip}:{port}" + proxy_urls, _ = parse_proxy_response(text) + + if not proxy_urls: + logger.warning(f"无法解析代理地址: {text}") + return None + + if len(proxy_urls) == 1: + proxy = proxy_urls[0] self.current_proxy = proxy logger.info(f"获取到代理: {proxy}") return proxy - logger.warning(f"无法解析代理地址: {text}") + # 多个代理逐一验证,返回第一个可用的 + logger.info(f"获取到 {len(proxy_urls)} 个代理,逐一验证") + for proxy in proxy_urls: + if self.verify_proxy(proxy): + self.current_proxy = proxy + logger.success(f"可用代理: {proxy}") + return proxy + logger.warning(f"代理 {proxy} 不可用,尝试下一个") + + logger.warning("所有代理均不可用") + self.current_proxy = None return None except Exception as e: @@ -79,32 +92,35 @@ def get_proxy_manager(api_url: str = "") -> ProxyManager: return ProxyManager(api_url) -def parse_proxy_response(text: str) -> tuple[Optional[str], Optional[str]]: +def parse_proxy_response(text: str) -> tuple[list[str], Optional[str]]: """ - 解析代理API响应。 + 解析代理API响应,支持返回多个代理地址。 Returns: - (proxy_url, whitelist_ip) - - proxy_url: 解析到的代理地址(http://ip:port),无法解析时为 None + (proxy_urls, whitelist_ip) + - proxy_urls: 解析到的所有代理地址列表(http://ip:port) - whitelist_ip: 需要添加到白名单的IP(当API返回白名单错误时),无错误时为 None """ text = text.strip() - # 正常代理地址 - match = re.search(r'(\d+\.\d+\.\d+\.\d+):(\d+)', text) - if match: - ip, port = match.group(1), match.group(2) - # 排除 "请先添加白名单:1.2.3.4" 中误匹配到 ip:port 的情况 - if '白名单' not in text: - return f"http://{ip}:{port}", None - - # 白名单错误:DB1.请先添加白名单:39.144.114.76 + # 白名单错误优先检测 if '添加白名单' in text or '白名单' in text: ip_match = re.search(r'(\d+\.\d+\.\d+\.\d+)', text) if ip_match: - return None, ip_match.group(1) + return [], ip_match.group(1) - return None, None + # 解析所有 ip:port,支持多行格式 + matches = re.findall(r'(\d+\.\d+\.\d+\.\d+):(\d+)', text) + proxies = [] + for ip, port in matches: + # 排除白名单提示中误匹配的 + proxy = f"http://{ip}:{port}" + proxies.append(proxy) + + if proxies: + return proxies, None + + return [], None def verify_proxy_url(proxy_url: str, timeout: tuple = (4, 6)) -> tuple[bool, str]: @@ -157,12 +173,13 @@ def resolve_working_proxy( ) -> tuple[Optional[str], str]: """ 从代理API获取可用代理,自动处理白名单同步。 + 支持API返回多个代理IP,逐一验证直到找到可用的。 Args: api_url: 代理API地址 whitelist_uid: 白名单UID(启用白名单时传入) whitelist_ukey: 白名单UKEY - max_attempts: 最大尝试次数(默认3次) + max_attempts: 最大获取尝试次数(默认3次) log_func: 日志回调函数 (level, message) Returns: @@ -180,7 +197,7 @@ def resolve_working_proxy( for attempt in range(1, max_attempts + 1): # 重试之间增加退避延迟,避免代理API返回同一个不可用IP if attempt > 1: - delay = min(attempt, 3) # 1s, 2s, 3s 上限 + delay = min(attempt, 3) log('info', f'等待 {delay}s 后重试...') time.sleep(delay) @@ -190,16 +207,18 @@ def resolve_working_proxy( response.raise_for_status() text = response.text.strip() - proxy_url, whitelist_ip = parse_proxy_response(text) + proxy_urls, whitelist_ip = parse_proxy_response(text) - if proxy_url: - # 验证代理 - ok, msg = verify_proxy_url(proxy_url) - if ok: - log('success', f'代理预检成功: {proxy_url}') - return proxy_url, msg - last_error = msg - log('warning', f'代理预检 {attempt}/{max_attempts}: {msg}') + if proxy_urls: + log('info', f'获取到 {len(proxy_urls)} 个代理,逐一验证') + for proxy_url in proxy_urls: + ok, msg = verify_proxy_url(proxy_url) + if ok: + log('success', f'代理预检成功: {proxy_url}') + return proxy_url, msg + log('warning', f'代理 {proxy_url} 不可用: {msg}') + last_error = f'共 {len(proxy_urls)} 个代理均不可用' + log('warning', f'代理预检 {attempt}/{max_attempts}: {last_error}') continue # 代理API返回白名单错误