#!/usr/bin/env python3 # -*- coding: utf-8 -*- from __future__ import annotations import re import time from typing import Optional import requests from app_logger import get_logger logger = get_logger("proxy") def validate_ip_port(ip_port_str: str) -> bool: """验证IP:PORT格式是否有效(含范围校验)。""" pattern = re.compile(r"^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3}):(\d{1,5})$") match = pattern.match(ip_port_str or "") if not match: return False for i in range(1, 5): octet = int(match.group(i)) if octet < 0 or octet > 255: return False port = int(match.group(5)) return 1 <= port <= 65535 def get_proxy_from_api(api_url: str, max_retries: int = 3) -> Optional[str]: """从API获取代理IP(支持重试)。""" ip_port_pattern = re.compile(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{1,5}$") max_retries = max(1, int(max_retries or 1)) for attempt in range(max_retries): try: response = requests.get(api_url, timeout=10) if response.status_code == 200: text = response.text.strip() # 尝试解析JSON响应 try: import json data = json.loads(text) if isinstance(data, dict): if data.get("status") not in (200, 0, None): error_msg = data.get("msg", data.get("message", "未知错误")) logger.warning(f"代理API返回错误: {error_msg} (尝试 {attempt + 1}/{max_retries})") if attempt < max_retries - 1: time.sleep(1) continue ip_port = data.get("ip") or data.get("proxy") or data.get("data") if ip_port: text = str(ip_port).strip() except Exception: pass if ip_port_pattern.match(text) and validate_ip_port(text): proxy_server = f"http://{text}" logger.info(f"获取代理成功: {proxy_server} (尝试 {attempt + 1}/{max_retries})") return proxy_server logger.warning(f"代理格式或范围无效: {text[:50]} (尝试 {attempt + 1}/{max_retries})") else: logger.warning(f"获取代理失败: HTTP {response.status_code} (尝试 {attempt + 1}/{max_retries})") except Exception as e: logger.warning(f"获取代理异常: {str(e)} (尝试 {attempt + 1}/{max_retries})") if attempt < max_retries - 1: time.sleep(1) logger.warning(f"获取代理失败,已重试 {max_retries} 次,将不使用代理继续") return None