commit 366a84fa89bca0edbd76f09e174e5ac47fbf9f28 Author: MasonLiu <2857911564@qq.com> Date: Mon Jun 15 00:49:26 2026 +0800 beta 1.0 diff --git a/README.md b/README.md new file mode 100644 index 0000000..bff3cd1 --- /dev/null +++ b/README.md @@ -0,0 +1,267 @@ +# WhereAmI - 多协议轮转代理工具 + +## 项目简介 + +WhereAmI 是一个基于 Python 和 PyQt6 的现代化多协议轮转代理工具,能够将各种外部代理(HTTP/HTTPS/SOCKS4/SOCKS5)通过本地 SOCKS5 转发服务部署到 `127.0.0.1:8745`,配合 Proxifier 等工具使用。 + +## 核心特性 + +### 🚀 三步工作流程 +1. **验证代理**:并发测试所有代理的连通性,对 SOCKS5 代理进行握手验证 +2. **获取代理**:从验证通过的代理列表中选择可用代理作为上游代理 +3. **转发到 8745 端口**:启动 SOCKS5 服务器监听 8745 端口,双向转发数据 + +### 🎯 主要功能 +- ✅ **多协议支持**:HTTP、HTTPS、SOCKS4、SOCKS5 +- ✅ **自动获取代理**:从指定网页抓取代理列表 +- ✅ **本地文件支持**:从 JSON 文件加载代理配置 +- ✅ **健康检测**:TCP 延迟测试 + Google 连通性测试 +- ✅ **代理轮转**:手动切换 / 自动定时切换 +- ✅ **实时状态监控**:GUI 界面显示代理状态、延迟、统计信息 +- ✅ **日志记录**:详细的运行日志,支持文件和控制台输出 + +## 技术栈 + +- **开发语言**: Python 3.9+ +- **GUI 框架**: PyQt6 +- **网络请求**: requests, aiohttp +- **HTML 解析**: BeautifulSoup4, lxml +- **配置文件**: PyYAML +- **SOCKS 支持**: PySocks +- **日志系统**: loguru +- **加密**: cryptography + +## 安装步骤 + +### 1. 克隆项目 +```bash +git clone +cd WhereAmI +``` + +### 2. 安装依赖 +```bash +pip install -r requirements.txt +``` + +### 3. 运行程序 +```bash +python main.py +``` + +## 使用说明 + +### 快速开始 + +1. **启动程序** + ```bash + python main.py + ``` + +2. **配置代理源** + - 点击 "⚙️ 配置" 按钮 + - 设置自动获取 URL(默认已配置为泰国代理网站) + - 或启用本地文件模式 + +3. **获取代理** + - 点击 "🌐 获取免费代理" 按钮 + - 程序会自动从网页抓取并检测代理 + +4. **启动服务** + - 点击 "▶ 开始服务" 按钮 + - 服务将在 `127.0.0.1:8745` 启动 SOCKS5 代理 + +5. **配置 Proxifier** + - 打开 Proxifier + - 添加代理服务器:`127.0.0.1:8745`,类型选择 SOCKS5 + - 设置代理规则,将需要代理的应用添加到规则中 + +6. **切换代理** + - 手动切换:点击 "🔄 切换下一个" 按钮 + - 自动切换:在配置中设置为 auto 模式 + +### 配置说明 + +配置文件 `config.yaml` 包含以下主要配置项: + +```yaml +# 代理源配置 +proxy_sources: + auto_fetch: + enabled: true # 是否启用自动获取 + url: "http://cn.freevpnnode.com/free-proxy-for-thailand/" # 抓取URL + refresh_interval: 10 # 刷新间隔(分钟) + local_file: + enabled: true # 是否启用本地文件 + path: "local.json" # 本地文件路径 + +# 轮转策略 +rotation: + mode: "manual" # manual | auto + auto_switch_interval: 300 # 自动切换间隔(秒) + latency_threshold: 500 # 延迟阈值(毫秒) + +# 输出配置 +output: + host: "127.0.0.1" # 监听地址 + port: 8745 # 监听端口 + +# 检测配置 +health_check: + timeout: 5 # TCP连接超时(秒) + connectivity_test_url: "https://www.google.com/" + connectivity_timeout: 10 # 连通性测试超时(秒) + max_failures: 3 # 最大失败次数 + retry_delay: 600 # 重试延迟(秒) +``` + +### 本地代理文件格式 + +项目使用两个JSON文件来管理代理: + +#### 1. `proxy.json` - 自动抓取的代理(自动生成) +此文件由程序自动创建,保存从网页抓取的代理列表。**不要手动编辑此文件**,每次获取新代理时会被覆盖。 + +#### 2. `local.json` - 用户手动添加的代理(推荐) +此文件用于存放您自己的可靠代理。格式示例: + +```json +[ + { + "ip_address": "192.168.1.100", + "port": 1080, + "username": "no need", + "password": "no need", + "protocol": "socks5", + "country": "CN", + "anonymity": "elite", + "speed": "fast", + "uptime_percentage": "99%", + "response_time": "100ms", + "latency": "50ms", + "last_updated": "now" + } +] +``` + +**必填字段:** +- `ip_address`: 代理 IP 地址 +- `port`: 代理端口 +- `username`: 用户名(无认证填 "no need") +- `password`: 密码(无认证填 "no need") +- `protocol`: 协议类型(http/https/socks4/socks5) + +**可选字段:** +- `country`: 国家代码 +- `anonymity`: 匿名级别 +- `speed`: 速度描述 +- `uptime_percentage`: 在线率 +- `response_time`: 响应时间 +- `latency`: 延迟 +- `last_updated`: 最后更新时间 + +## GUI 界面说明 + +### 主界面布局 + +#### 顶部状态栏 +- **状态指示器**:显示服务运行状态(运行中/已停止) +- **当前代理信息**:显示正在使用的代理 IP、端口、协议、延迟 +- **统计信息**:显示总代理数、可用数、优秀数、不可用数 + +#### 控制按钮区 +- **▶ 开始服务**:启动 SOCKS5 代理服务(需要先选择一个代理节点) +- **⏹ 停止服务**:停止代理服务 +- **🔄 切换下一个**:切换到下一个可用代理 +- **🌐 获取免费代理**:从网页抓取代理并检测(默认功能) +- **📁 使用本地代理**:仅加载 `local.json` 中的代理 +- **🔧 其他代理(预留)**:暂未实现,预留用于后续测试 +- **⚙️ 配置**:打开配置对话框 + +#### 标签页 +1. **代理列表**:显示所有代理的详细信息表格 + - 列:IP地址、端口、协议、国家、状态、延迟、匿名级别、速度、运行时间、最后更新 + - 状态颜色:🟢 优秀、🟡 可用、🔴 不可用、⚪ 未知 + - **过滤选项**:"显示不可用代理"复选框,控制是否显示不可用的代理 + - **选择代理**:点击表格行来选择要使用的代理节点 + +2. **连接日志**:显示所有通过代理的连接记录 + - 格式:`[时间戳] 本地IP:端口 -> 远程代理IP:端口` + - 示例:`[2026-06-14 22:30:15] 127.0.0.1:54321 -> 192.168.1.100:1080` + - 支持清空日志 + +3. **日志**:显示程序运行日志 + - 支持清空日志 + - 显示时间戳和详细操作信息 + +## 常见问题 + +### Q1: 如何测试代理是否工作? +A: 启动服务后,使用 curl 命令测试: +```bash +curl -x socks5://127.0.0.1:8745 https://ip.sb +``` + +### Q2: 为什么有些代理显示为不可用? +A: 代理可能因为以下原因被标记为不可用: +- TCP 连接超时(>5秒) +- Google 连通性测试失败 +- 连续失败 3 次以上 + +### Q3: 如何添加自己的代理? +A: 编辑 `local.json` 文件,按照格式添加您的可靠代理信息。程序会自动合并 `proxy.json`(抓取的代理)和 `local.json`(手动添加的代理)。建议优先使用 `local.json` 添加稳定可靠的代理。 + +### Q4: 端口 8745 被占用怎么办? +A: 在配置中修改监听端口,或使用以下命令查找占用进程: +```bash +netstat -ano | findstr "8745" +``` + +### Q5: 如何实现自动切换代理? +A: 在配置中将 `rotation.mode` 设置为 `"auto"`,并设置 `auto_switch_interval` 为期望的切换间隔(秒)。 + +## 项目结构 + +``` +WhereAmI/ +├── main.py # 主入口 +├── config.yaml # 配置文件 +├── proxy.json # 自动抓取的代理(程序自动生成) +├── local.json # 用户手动添加的代理(推荐) +├── requirements.txt # 依赖包 +├── README.md # 项目说明 +├── core/ # 核心逻辑 +│ ├── __init__.py +│ ├── models.py # 数据模型 +│ ├── data_source.py # 数据源获取 +│ ├── health_checker.py # 健康检测器 +│ ├── proxy_manager.py # 代理管理器 +│ └── simple_proxy_forwarder.py # 简化转发器 +├── gui/ # GUI界面 +│ ├── __init__.py +│ └── main_window.py # 主窗口 +├── utils/ # 工具类 +│ └── __init__.py +└── logs/ # 日志目录 + └── whereami.log +``` + +## 注意事项 + +1. **首次运行**:请确保安装了所有依赖包 +2. **代理质量**:免费代理稳定性较差,建议定期更新代理列表 +3. **安全性**:不要在免费代理上登录敏感账户或传输机密数据 +4. **法律合规**:请遵守当地法律法规,合理使用代理服务 + +## 许可证 + +本项目仅供学习和研究使用。 + +## 贡献 + +欢迎提交 Issue 和 Pull Request! + +--- + +**版本**: v1.0.0 +**更新日期**: 2026-06-14 diff --git a/config.yaml b/config.yaml new file mode 100644 index 0000000..60427a6 --- /dev/null +++ b/config.yaml @@ -0,0 +1,17 @@ +output: + host: 127.0.0.1 + port: 8745 +proxy_sources: + auto_fetch: + enabled: true + output_file: proxy.json + pages: 3 + refresh_interval: 10 + url: http://cn.freevpnnode.com/free-proxy-for-thailand + local_file: + enabled: true + path: local.json +rotation: + auto_switch_interval: 300 + latency_threshold: 500 + mode: manual diff --git a/core/__init__.py b/core/__init__.py new file mode 100644 index 0000000..fd91416 --- /dev/null +++ b/core/__init__.py @@ -0,0 +1,16 @@ +"""WhereAmI 核心模块""" +from .models import ProxyInfo, ProxyProtocol, ProxyStatus +from .data_source import DataSource +from .health_checker import HealthChecker +from .proxy_manager import ProxyManager +from .simple_proxy_forwarder import SimpleProxyForwarder + +__all__ = [ + 'ProxyInfo', + 'ProxyProtocol', + 'ProxyStatus', + 'DataSource', + 'HealthChecker', + 'ProxyManager', + 'SimpleProxyForwarder' +] diff --git a/core/data_source.py b/core/data_source.py new file mode 100644 index 0000000..85ffdfb --- /dev/null +++ b/core/data_source.py @@ -0,0 +1,219 @@ +"""代理数据源获取模块""" +import requests +from bs4 import BeautifulSoup +import json +import os +from typing import List, Optional +from datetime import datetime +from loguru import logger +from .models import ProxyInfo, ProxyProtocol + + +class DataSource: + """代理数据源管理器""" + + def __init__(self, config: dict): + self.config = config + self.logger = logger + + def fetch_from_web(self, url: str, output_file: str = "proxy.json", pages: int = 1) -> List[ProxyInfo]: + """从网页获取代理列表并保存到文件 + + Args: + url: 基础URL(不含页码参数) + output_file: 输出文件路径 + pages: 抓取的页数,默认1页 + """ + self.logger.info(f"开始从网页获取代理: {url} (共{pages}页)") + all_proxies = [] + + try: + headers = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' + } + + # 抓取多页 + for page in range(1, pages + 1): + # 构建带页码的URL + if '?' in url: + page_url = f"{url}&page={page}" + else: + page_url = f"{url}?page={page}" + + self.logger.info(f"正在获取第 {page}/{pages} 页: {page_url}") + + try: + response = requests.get(page_url, headers=headers, timeout=30) + response.encoding = 'utf-8' + + if response.status_code != 200: + self.logger.warning(f"第{page}页获取失败,状态码: {response.status_code}") + continue + + proxies = self._parse_html(response.text) + self.logger.info(f"第{page}页成功获取 {len(proxies)} 个代理") + all_proxies.extend(proxies) + + except Exception as e: + self.logger.error(f"第{page}页获取失败: {str(e)}") + continue + + # 去重(基于IP+端口) + seen = set() + unique_proxies = [] + for proxy in all_proxies: + key = f"{proxy.ip_address}:{proxy.port}" + if key not in seen: + seen.add(key) + unique_proxies.append(proxy) + + self.logger.info(f"成功获取 {len(unique_proxies)} 个唯一代理(共{len(all_proxies)}个,去重后)") + + # 保存到文件 + if unique_proxies: + self.save_to_file(unique_proxies, output_file) + self.logger.info(f"代理已保存到: {output_file}") + + except Exception as e: + self.logger.error(f"获取网页代理失败: {str(e)}") + + return unique_proxies + + def _parse_html(self, html: str) -> List[ProxyInfo]: + """解析HTML提取代理信息(基于page.html结构)""" + proxies = [] + soup = BeautifulSoup(html, 'html.parser') + + # 查找表格 + table = soup.find('table', class_='table') + if not table: + self.logger.warning("未找到代理表格") + return proxies + + # 解析表格行 + tbody = table.find('tbody') + if not tbody: + return proxies + + rows = tbody.find_all('tr') + for row in rows: + try: + cells = row.find_all('td') + if len(cells) < 12: + continue + + # 提取字段(根据page.html结构) + ip_address = cells[0].text.strip() + port_text = cells[1].text.strip() + + # 用户名和密码隐藏在img的data-text属性中 + username_img = cells[2].find('img') + username = username_img.get('data-text', 'no need') if username_img else 'no need' + + password_img = cells[3].find('img') + password = password_img.get('data-text', 'no need') if password_img else 'no need' + + # 国家 + country_link = cells[4].find('a') + country = country_link.get('title', '') if country_link else '' + + # 协议 + protocol_text = cells[5].text.strip().lower() + + # 匿名级别 + anonymity = cells[6].text.strip() + + # 速度 + speed = cells[7].text.strip() + + # 运行时间百分比 + uptime = cells[8].text.strip() + + # 响应时间 + response_time = cells[9].text.strip() + + # 延迟 + latency = cells[10].text.strip() + + # 更新时间 + last_updated = cells[11].text.strip() + + # 验证必要字段 + if not ip_address or not port_text: + continue + + # 转换端口为整数 + try: + port = int(port_text) + except ValueError: + continue + + # 映射协议 + protocol_map = { + 'http': ProxyProtocol.HTTP, + 'https': ProxyProtocol.HTTPS, + 'socks4': ProxyProtocol.SOCKS4, + 'socks5': ProxyProtocol.SOCKS5 + } + protocol = protocol_map.get(protocol_text, ProxyProtocol.SOCKS5) + + proxy = ProxyInfo( + ip_address=ip_address, + port=port, + username=username, + password=password, + protocol=protocol, + country=country, + anonymity=anonymity, + speed=speed, + uptime_percentage=uptime, + response_time=response_time, + latency=latency, + last_updated=last_updated + ) + proxies.append(proxy) + + except Exception as e: + self.logger.debug(f"解析代理行失败: {str(e)}") + continue + + return proxies + + def load_from_file(self, filepath: str) -> List[ProxyInfo]: + """从本地文件加载代理列表""" + self.logger.info(f"从本地文件加载代理: {filepath}") + proxies = [] + + if not os.path.exists(filepath): + self.logger.warning(f"文件不存在: {filepath}") + return proxies + + try: + with open(filepath, 'r', encoding='utf-8') as f: + data = json.load(f) + + if isinstance(data, list): + for item in data: + try: + proxy = ProxyInfo.from_dict(item) + proxies.append(proxy) + except Exception as e: + self.logger.debug(f"解析代理项失败: {str(e)}") + continue + + self.logger.info(f"成功加载 {len(proxies)} 个代理") + + except Exception as e: + self.logger.error(f"加载本地文件失败: {str(e)}") + + return proxies + + def save_to_file(self, proxies: List[ProxyInfo], filepath: str): + """保存代理列表到文件""" + try: + data = [proxy.to_dict() for proxy in proxies] + with open(filepath, 'w', encoding='utf-8') as f: + json.dump(data, f, indent=2, ensure_ascii=False) + self.logger.info(f"保存 {len(proxies)} 个代理到文件: {filepath}") + except Exception as e: + self.logger.error(f"保存文件失败: {str(e)}") diff --git a/core/health_checker.py b/core/health_checker.py new file mode 100644 index 0000000..b689f15 --- /dev/null +++ b/core/health_checker.py @@ -0,0 +1,192 @@ +"""代理健康检测模块""" +import socket +import requests +import threading +import time +from typing import List, Callable, Optional +from concurrent.futures import ThreadPoolExecutor, as_completed +from datetime import datetime, timedelta +from loguru import logger +from .models import ProxyInfo, ProxyStatus + + +class HealthChecker: + """代理健康检测器""" + + def __init__(self, config: dict): + self.config = config + self.logger = logger + self.health_config = config.get('health_check', {}) + + self.timeout = self.health_config.get('timeout', 5) + self.connectivity_url = self.health_config.get('connectivity_test_url', 'https://www.google.com/') + self.connectivity_timeout = self.health_config.get('connectivity_timeout', 10) + self.max_failures = self.health_config.get('max_failures', 3) + self.retry_delay = self.health_config.get('retry_delay', 600) + + self._check_lock = threading.Lock() + self._executor = ThreadPoolExecutor(max_workers=10) + + def check_proxy(self, proxy: ProxyInfo) -> ProxyStatus: + """检测单个代理的健康状态""" + with self._check_lock: + proxy.status = ProxyStatus.CHECKING + proxy.last_check = datetime.now() + + try: + # 步骤1: TCP连接测试(快速检查) + start_time = time.time() + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.settimeout(self.timeout) + + result = sock.connect_ex((proxy.ip_address, proxy.port)) + tcp_elapsed = (time.time() - start_time) * 1000 # 转换为毫秒 + + sock.close() + + if result != 0: + raise ConnectionError(f"无法连接到 {proxy.get_address()}") + + proxy.latency_ms = tcp_elapsed + + # 步骤2: 测试Google连通性 + if not self._test_connectivity(proxy): + raise ConnectionError("Google连通性测试失败") + + # 步骤3: 连通性成功后,再次测试真实延迟 + start_time = time.time() + sock2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock2.settimeout(self.timeout) + + result2 = sock2.connect_ex((proxy.ip_address, proxy.port)) + real_elapsed = (time.time() - start_time) * 1000 # 转换为毫秒 + + sock2.close() + + if result2 == 0: + # 使用第二次测试的真实延迟 + proxy.latency_ms = real_elapsed + + # 判断等级(基于真实延迟) + if real_elapsed < 200: + proxy.status = ProxyStatus.EXCELLENT + else: + proxy.status = ProxyStatus.AVAILABLE + proxy.consecutive_failures = 0 + + except Exception as e: + proxy.consecutive_failures += 1 + proxy.latency_ms = 9999 + proxy.status = ProxyStatus.UNAVAILABLE + + self.logger.debug(f"代理 {proxy.get_address()} 检测失败: {str(e)}") + + return proxy.status + + def _test_connectivity(self, proxy: ProxyInfo) -> bool: + """测试代理的Google连通性 + + 使用多种方法测试,提高准确性: + 1. 首先尝试 HEAD 请求(类似 curl -I) + 2. 如果失败,尝试 GET 请求 + 3. 支持多个测试URL + """ + test_urls = [ + 'https://www.google.com/', + 'https://www.google.com/generate_204', # 专门用于连通性测试 + 'http://www.google.com/', + ] + + for test_url in test_urls: + try: + proxies_dict = {} + protocol = proxy.protocol.value + + # 构建代理配置 + if protocol in ['http', 'https']: + auth = proxy.get_auth_string() + if auth: + proxies_dict['http'] = f"http://{auth}@{proxy.get_address()}" + proxies_dict['https'] = f"http://{auth}@{proxy.get_address()}" + else: + proxies_dict['http'] = f"http://{proxy.get_address()}" + proxies_dict['https'] = f"http://{proxy.get_address()}" + elif protocol in ['socks4', 'socks5']: + auth = proxy.get_auth_string() + proxy_url = f"socks5://{auth}@{proxy.get_address()}" if auth else f"socks5://{proxy.get_address()}" + proxies_dict['http'] = proxy_url + proxies_dict['https'] = proxy_url + + # 方法1: 尝试 HEAD 请求(轻量,类似 curl -I) + try: + response = requests.head( + test_url, + proxies=proxies_dict, + timeout=self.connectivity_timeout, + verify=False, + allow_redirects=True + ) + # HEAD 请求成功即可 + if response.status_code < 400: + self.logger.debug(f"代理 {proxy.get_address()} HEAD测试成功: {test_url} ({response.status_code})") + return True + except Exception as e: + self.logger.debug(f"HEAD测试失败: {str(e)}") + + # 方法2: 如果 HEAD 失败,尝试 GET 请求 + try: + response = requests.get( + test_url, + proxies=proxies_dict, + timeout=self.connectivity_timeout, + verify=False, + allow_redirects=True + ) + # GET 请求成功 + if response.status_code < 400: + self.logger.debug(f"代理 {proxy.get_address()} GET测试成功: {test_url} ({response.status_code})") + return True + except Exception as e: + self.logger.debug(f"GET测试失败: {str(e)}") + continue + + except Exception as e: + self.logger.debug(f"测试URL {test_url} 失败: {str(e)}") + continue + + # 所有测试都失败 + self.logger.debug(f"代理 {proxy.get_address()} 所有连通性测试均失败") + return False + + def batch_check(self, proxies: List[ProxyInfo], callback: Callable = None) -> List[ProxyInfo]: + """批量检测代理""" + self.logger.info(f"开始批量检测 {len(proxies)} 个代理") + + futures = {} + for proxy in proxies: + # 跳过最近检测过的代理(缓存60秒) + if proxy.last_check and (datetime.now() - proxy.last_check) < timedelta(seconds=60): + continue + + future = self._executor.submit(self.check_proxy, proxy) + futures[future] = proxy + + checked_proxies = [] + for future in as_completed(futures): + proxy = futures[future] + try: + future.result() + checked_proxies.append(proxy) + + if callback: + callback(proxy) + + except Exception as e: + self.logger.error(f"检测代理 {proxy.get_address()} 时出错: {str(e)}") + + self.logger.info(f"完成检测,共检测 {len(checked_proxies)} 个代理") + return checked_proxies + + def shutdown(self): + """关闭检测器""" + self._executor.shutdown(wait=False) diff --git a/core/models.py b/core/models.py new file mode 100644 index 0000000..ebbcc04 --- /dev/null +++ b/core/models.py @@ -0,0 +1,98 @@ +"""数据模型定义""" +from dataclasses import dataclass, field +from typing import Optional +from enum import Enum +from datetime import datetime + + +class ProxyProtocol(Enum): + """代理协议枚举""" + HTTP = "http" + HTTPS = "https" + SOCKS4 = "socks4" + SOCKS5 = "socks5" + + +class ProxyStatus(Enum): + """代理状态枚举""" + UNKNOWN = "unknown" + CHECKING = "checking" + AVAILABLE = "available" + EXCELLENT = "excellent" + UNAVAILABLE = "unavailable" + + +@dataclass +class ProxyInfo: + """代理信息数据类""" + ip_address: str + port: int + username: str = "no need" + password: str = "no need" + protocol: ProxyProtocol = ProxyProtocol.SOCKS5 + country: str = "" + anonymity: str = "" + speed: str = "" + uptime_percentage: str = "" + response_time: str = "" + latency: str = "" + last_updated: str = "" + + # 运行时状态 + status: ProxyStatus = ProxyStatus.UNKNOWN + latency_ms: float = 0.0 + last_check: Optional[datetime] = None + consecutive_failures: int = 0 + is_active: bool = False + + def to_dict(self) -> dict: + """转换为字典""" + return { + 'ip_address': self.ip_address, + 'port': self.port, + 'username': self.username, + 'password': self.password, + 'protocol': self.protocol.value, + 'country': self.country, + 'anonymity': self.anonymity, + 'speed': self.speed, + 'uptime_percentage': self.uptime_percentage, + 'response_time': self.response_time, + 'latency': self.latency, + 'last_updated': self.last_updated + } + + @classmethod + def from_dict(cls, data: dict) -> 'ProxyInfo': + """从字典创建""" + protocol_map = { + 'http': ProxyProtocol.HTTP, + 'https': ProxyProtocol.HTTPS, + 'socks4': ProxyProtocol.SOCKS4, + 'socks5': ProxyProtocol.SOCKS5 + } + + return cls( + ip_address=data['ip_address'], + port=int(data['port']), + username=data.get('username', 'no need'), + password=data.get('password', 'no need'), + protocol=protocol_map.get(data.get('protocol', 'socks5'), ProxyProtocol.SOCKS5), + country=data.get('country', ''), + anonymity=data.get('anonymity', ''), + speed=data.get('speed', ''), + uptime_percentage=data.get('uptime_percentage', ''), + response_time=data.get('response_time', ''), + latency=data.get('latency', ''), + last_updated=data.get('last_updated', '') + ) + + def get_address(self) -> str: + """获取代理地址字符串""" + return f"{self.ip_address}:{self.port}" + + def get_auth_string(self) -> Optional[str]: + """获取认证字符串""" + if self.username and self.username != "no need": + return f"{self.username}:{self.password}" + return None diff --git a/core/proxy_manager.py b/core/proxy_manager.py new file mode 100644 index 0000000..e57f59a --- /dev/null +++ b/core/proxy_manager.py @@ -0,0 +1,266 @@ +"""代理管理器 - 协调数据源、检测器和转发器""" +import threading +import time +import json +import os +from typing import List, Optional +from datetime import datetime +from loguru import logger +from .models import ProxyInfo, ProxyStatus +from .data_source import DataSource +from .health_checker import HealthChecker +from .simple_proxy_forwarder import SimpleProxyForwarder + + +class ProxyManager: + """代理管理器""" + + def __init__(self, config: dict, connection_log_callback=None): + self.config = config + self.logger = logger + + self.data_source = DataSource(config) + self.health_checker = HealthChecker(config) + self.forwarder = SimpleProxyForwarder(config, connection_log_callback) + + self.proxies: List[ProxyInfo] = [] + self.active_proxy: Optional[ProxyInfo] = None + self.available_proxies: List[ProxyInfo] = [] + + self._lock = threading.RLock() + self._running = False + self._refresh_thread = None + + def load_proxies(self) -> int: + """加载代理列表""" + with self._lock: + all_proxies = [] + + # 从网页获取(保存到 proxy.json) + if self.config.get('proxy_sources', {}).get('auto_fetch', {}).get('enabled', True): + url = self.config['proxy_sources']['auto_fetch'].get('url') + output_file = self.config['proxy_sources']['auto_fetch'].get('output_file', 'proxy.json') + pages = self.config['proxy_sources']['auto_fetch'].get('pages', 1) # 获取页数配置 + if url: + web_proxies = self.data_source.fetch_from_web(url, output_file, pages) + all_proxies.extend(web_proxies) + + # 从本地文件加载(用户手动添加的 local.json) + if self.config.get('proxy_sources', {}).get('local_file', {}).get('enabled', True): + filepath = self.config['proxy_sources']['local_file'].get('path', 'local.json') + file_proxies = self.data_source.load_from_file(filepath) + all_proxies.extend(file_proxies) + + # 去重(基于IP+端口) + seen = set() + unique_proxies = [] + for proxy in all_proxies: + key = f"{proxy.ip_address}:{proxy.port}" + if key not in seen: + seen.add(key) + unique_proxies.append(proxy) + + self.proxies = unique_proxies + self.logger.info(f"加载了 {len(self.proxies)} 个唯一代理") + + return len(self.proxies) + + def set_proxies(self, proxies: List[ProxyInfo]): + """设置代理列表(用于GUI选择单个代理)""" + with self._lock: + self.proxies = proxies + self.logger.info(f"设置代理列表: {len(proxies)} 个代理") + + def check_all_proxies(self) -> int: + """检测所有代理""" + with self._lock: + available_count = 0 + + def on_check_complete(proxy: ProxyInfo): + nonlocal available_count + if proxy.status in [ProxyStatus.AVAILABLE, ProxyStatus.EXCELLENT]: + available_count += 1 + + self.health_checker.batch_check(self.proxies, callback=on_check_complete) + + # 更新可用代理列表 + self.available_proxies = [ + p for p in self.proxies + if p.status in [ProxyStatus.AVAILABLE, ProxyStatus.EXCELLENT] + ] + + # 同步延迟到本地文件 + self._sync_latency_to_file() + + self.logger.info(f"检测到 {available_count} 个可用代理") + return available_count + + def _sync_latency_to_file(self): + """将检测结果(延迟)同步到本地文件 + + 同时同步到: + 1. proxy.json(网页抓取的代理) + 2. local.json(用户手动添加的代理) + """ + try: + # 同步到 proxy.json + self._sync_to_single_file( + self.config.get('proxy_sources', {}).get('auto_fetch', {}).get('output_file', 'proxy.json') + ) + + # 同步到 local.json + local_file_path = self.config.get('proxy_sources', {}).get('local_file', {}).get('path', 'local.json') + if local_file_path and local_file_path != 'proxy.json': + self._sync_to_single_file(local_file_path) + + except Exception as e: + self.logger.error(f"同步延迟到文件失败: {str(e)}") + + def _sync_to_single_file(self, filepath: str): + """同步延迟到单个文件""" + try: + if not os.path.exists(filepath): + return + + # 读取现有文件 + with open(filepath, 'r', encoding='utf-8') as f: + data = json.load(f) + + if not isinstance(data, list): + return + + # 创建索引 + existing_proxies = {} + for item in data: + key = f"{item['ip_address']}:{item['port']}" + existing_proxies[key] = item + + # 更新延迟信息 + updated_count = 0 + for proxy in self.proxies: + key = proxy.get_address() + if key in existing_proxies: + # 更新延迟和状态 + existing_proxies[key]['latency'] = f"{proxy.latency_ms:.0f}ms" if proxy.latency_ms < 9999 else "timeout" + existing_proxies[key]['status'] = proxy.status.value + existing_proxies[key]['last_checked'] = datetime.now().isoformat() + updated_count += 1 + + # 保存更新后的数据 + if updated_count > 0: + updated_data = list(existing_proxies.values()) + with open(filepath, 'w', encoding='utf-8') as f: + json.dump(updated_data, f, indent=2, ensure_ascii=False) + self.logger.info(f"已同步 {updated_count} 个代理的延迟信息到 {filepath}") + + except Exception as e: + self.logger.debug(f"同步到 {filepath} 失败: {str(e)}") + + def start_service(self) -> bool: + """启动代理服务""" + if self._running: + self.logger.warning("服务已在运行") + return True + + try: + # 加载代理 + self.load_proxies() + + # 检测代理 + self.check_all_proxies() + + if not self.available_proxies: + self.logger.error("没有可用的代理,无法启动服务") + return False + + # 启动转发器 + self.forwarder.start(self.available_proxies) + + self._running = True + self.active_proxy = self.forwarder.current_proxy + + self.logger.info("代理服务已启动") + return True + + except Exception as e: + self.logger.error(f"启动服务失败: {str(e)}") + return False + + def stop_service(self): + """停止代理服务""" + if not self._running: + return + + self._running = False + self.forwarder.stop() + self.health_checker.shutdown() + + self.logger.info("代理服务已停止") + + def switch_to_next_proxy(self) -> bool: + """切换到下一个代理""" + if not self._running: + self.logger.warning("服务未运行") + return False + + if not self.available_proxies: + self.logger.warning("没有可用代理") + return False + + # 找到当前代理的索引 + current_index = -1 + if self.active_proxy and self.active_proxy in self.available_proxies: + current_index = self.available_proxies.index(self.active_proxy) + + # 选择下一个代理 + next_index = (current_index + 1) % len(self.available_proxies) + next_proxy = self.available_proxies[next_index] + + # 切换代理 + success = self.forwarder.switch_proxy(next_proxy) + if success: + self.active_proxy = next_proxy + self.logger.info(f"成功切换到下一个代理: {next_proxy.get_address()}") + + return success + + def get_active_proxy(self) -> Optional[ProxyInfo]: + """获取当前活跃代理""" + with self._lock: + return self.active_proxy + + def get_statistics(self) -> dict: + """获取统计信息""" + with self._lock: + stats = { + 'total': len(self.proxies), + 'available': len([p for p in self.proxies if p.status == ProxyStatus.AVAILABLE]), + 'excellent': len([p for p in self.proxies if p.status == ProxyStatus.EXCELLENT]), + 'unavailable': len([p for p in self.proxies if p.status == ProxyStatus.UNAVAILABLE]), + 'active': self.active_proxy.get_address() if self.active_proxy else None + } + return stats + + def start_auto_refresh(self, interval_minutes: int = 10): + """启动自动刷新""" + if self._running: + return + + self._running = True + + def refresh_loop(): + while self._running: + time.sleep(interval_minutes * 60) + if self._running: + self.logger.info("执行自动刷新...") + self.load_proxies() + self.check_all_proxies() + + self._refresh_thread = threading.Thread(target=refresh_loop, daemon=True) + self._refresh_thread.start() + + def stop_auto_refresh(self): + """停止自动刷新""" + self._running = False + if self._refresh_thread: + self._refresh_thread.join(timeout=5) diff --git a/core/simple_proxy_forwarder.py b/core/simple_proxy_forwarder.py new file mode 100644 index 0000000..89cf02f --- /dev/null +++ b/core/simple_proxy_forwarder.py @@ -0,0 +1,385 @@ +"""简化代理转发器 - 三步工作流程""" +import socket +import threading +import socks +import time +from typing import List, Optional +from concurrent.futures import ThreadPoolExecutor, as_completed +from loguru import logger +from .models import ProxyInfo, ProxyStatus + + +class SimpleProxyForwarder: + """简化代理转发器 + + 工作流程: + 1. 验证代理 (_validate_proxies) + 2. 获取可用代理 (_test_proxy) + 3. 转发到8745端口 (_run_forwarder) + """ + + def __init__(self, config: dict, connection_log_callback=None): + self.config = config + self.logger = logger + + output_config = config.get('output', {}) + self.host = output_config.get('host', '127.0.0.1') + self.port = output_config.get('port', 8745) + + self.validated_proxies: List[ProxyInfo] = [] + self.current_proxy: Optional[ProxyInfo] = None + self.server_socket: Optional[socket.socket] = None + self._running = False + self._server_thread: Optional[threading.Thread] = None + self._connections = [] + self._lock = threading.Lock() + self._executor = ThreadPoolExecutor(max_workers=10) + + # 连接日志回调函数 + self.connection_log_callback = connection_log_callback + + def validate_proxies(self, proxies: List[ProxyInfo]) -> List[ProxyInfo]: + """步骤1: 验证所有代理的连通性""" + self.logger.info(f"开始验证 {len(proxies)} 个代理") + self.validated_proxies = [] + + futures = {} + for proxy in proxies: + future = self._executor.submit(self._validate_single_proxy, proxy) + futures[future] = proxy + + for future in as_completed(futures): + proxy = futures[future] + try: + is_valid = future.result() + if is_valid: + self.validated_proxies.append(proxy) + proxy.status = ProxyStatus.AVAILABLE + except Exception as e: + self.logger.debug(f"验证代理 {proxy.get_address()} 失败: {str(e)}") + + self.logger.info(f"验证完成,{len(self.validated_proxies)} 个代理可用") + return self.validated_proxies + + def _validate_single_proxy(self, proxy: ProxyInfo) -> bool: + """验证单个代理""" + try: + # TCP连接测试 + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.settimeout(5) + + result = sock.connect_ex((proxy.ip_address, proxy.port)) + sock.close() + + if result != 0: + return False + + # SOCKS握手验证(针对SOCKS代理) + if proxy.protocol.value in ['socks4', 'socks5']: + return self._test_socks_handshake(proxy) + + return True + + except Exception: + return False + + def _test_socks_handshake(self, proxy: ProxyInfo) -> bool: + """测试SOCKS握手""" + try: + proxy_type = socks.SOCKS5 if proxy.protocol.value == 'socks5' else socks.SOCKS4 + + test_sock = socks.socksocket() + test_sock.set_proxy( + proxy_type, + proxy.ip_address, + proxy.port, + username=proxy.username if proxy.username != "no need" else None, + password=proxy.password if proxy.password != "no need" else None + ) + test_sock.settimeout(5) + + # 尝试连接到测试服务器 + test_sock.connect(('www.google.com', 80)) + test_sock.close() + + return True + + except Exception: + return False + + def get_available_proxy(self) -> Optional[ProxyInfo]: + """步骤2: 获取可用的代理""" + if not self.validated_proxies: + return None + + # 选择第一个可用代理(可以扩展为负载均衡策略) + return self.validated_proxies[0] + + def start(self, proxies: List[ProxyInfo]): + """启动转发器""" + if self._running: + self.logger.warning("转发器已在运行") + return + + # 步骤1: 验证代理 + self.validate_proxies(proxies) + + if not self.validated_proxies: + self.logger.error("没有可用的代理") + return + + # 步骤2: 获取代理 + self.current_proxy = self.get_available_proxy() + if not self.current_proxy: + self.logger.error("无法获取可用代理") + return + + self.logger.info(f"使用代理: {self.current_proxy.get_address()} ({self.current_proxy.protocol.value})") + + # 步骤3: 启动转发器 + self._run_forwarder() + + def _run_forwarder(self): + """步骤3: 运行SOCKS5转发服务器""" + try: + self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self.server_socket.bind((self.host, self.port)) + self.server_socket.listen(100) + self.server_socket.settimeout(1.0) + + self._running = True + self._server_thread = threading.Thread(target=self._accept_loop, daemon=True) + self._server_thread.start() + + self.logger.info(f"SOCKS5转发器已启动: {self.host}:{self.port}") + + except Exception as e: + self.logger.error(f"启动转发器失败: {str(e)}") + raise + + def _accept_loop(self): + """接受客户端连接循环""" + while self._running: + try: + client_socket, client_address = self.server_socket.accept() + + # 为新连接创建处理线程 + handler = ForwarderConnectionHandler( + client_socket, + client_address, + self.current_proxy, + self.logger, + self.connection_log_callback # 传递日志回调 + ) + handler.start() + + with self._lock: + self._connections.append(handler) + + except socket.timeout: + continue + except Exception as e: + if self._running: + self.logger.error(f"接受连接失败: {str(e)}") + break + + def stop(self): + """停止转发器""" + self._running = False + + # 关闭所有连接 + with self._lock: + for conn in self._connections: + try: + conn.running = False + conn.join(timeout=2) + except: + pass + self._connections.clear() + + if self.server_socket: + try: + self.server_socket.close() + except: + pass + + if self._server_thread: + self._server_thread.join(timeout=5) + + self._executor.shutdown(wait=False) + + self.logger.info("转发器已停止") + + def switch_proxy(self, proxy: ProxyInfo) -> bool: + """切换到新代理""" + if proxy not in self.validated_proxies: + self.logger.warning(f"代理 {proxy.get_address()} 未通过验证") + return False + + with self._lock: + self.current_proxy = proxy + self.logger.info(f"切换到代理: {proxy.get_address()}") + return True + + +class ForwarderConnectionHandler(threading.Thread): + """转发器连接处理器""" + + def __init__(self, client_socket: socket.socket, client_address, upstream_proxy: ProxyInfo, logger, connection_log_callback=None): + super().__init__(daemon=True) + self.client_socket = client_socket + self.client_address = client_address + self.upstream_proxy = upstream_proxy + self.logger = logger + self.running = True + self.connection_log_callback = connection_log_callback + + def run(self): + """处理客户端连接""" + try: + # SOCKS5握手 + if not self._socks5_handshake(): + self.logger.debug(f"SOCKS5握手失败: {self.client_address}") + return + + # 建立到目标服务器的连接 + target_socket = self._connect_to_target() + if not target_socket: + return + + # 记录连接日志 + if self.connection_log_callback: + local_addr = f"{self.client_address[0]}:{self.client_address[1]}" + remote_addr = f"{self.upstream_proxy.ip_address}:{self.upstream_proxy.port}" + log_message = f"{local_addr} -> {remote_addr}" + self.connection_log_callback(log_message) + + # 双向转发数据 + self._forward_data(target_socket) + + except Exception as e: + self.logger.debug(f"处理连接时出错: {str(e)}") + finally: + try: + self.client_socket.close() + except: + pass + self.running = False + + def _socks5_handshake(self) -> bool: + """执行SOCKS5握手""" + try: + version = self.client_socket.recv(1) + if version != b'\x05': + return False + + nmethods = ord(self.client_socket.recv(1)) + methods = self.client_socket.recv(nmethods) + + self.client_socket.send(b'\x05\x00') + + version = self.client_socket.recv(1) + if version != b'\x05': + return False + + cmd = self.client_socket.recv(1) + if cmd != b'\x01': + self.client_socket.send(b'\x05\x07\x00\x01\x00\x00\x00\x00\x00\x00') + return False + + self.client_socket.recv(1) + atype = self.client_socket.recv(1) + + if atype == b'\x01': + dest_addr = self.client_socket.recv(4) + dest_port = self.client_socket.recv(2) + elif atype == b'\x03': + addr_len_byte = self.client_socket.recv(1) + addr_len = addr_len_byte[0] if isinstance(addr_len_byte, bytes) else ord(addr_len_byte) + dest_addr = self.client_socket.recv(addr_len) + dest_port = self.client_socket.recv(2) + elif atype == b'\x04': + dest_addr = self.client_socket.recv(16) + dest_port = self.client_socket.recv(2) + else: + return False + + self.client_socket.send(b'\x05\x00\x00\x01\x00\x00\x00\x00\x00\x00') + + self.dest_addr = dest_addr + self.dest_port = dest_port + + return True + + except Exception as e: + self.logger.debug(f"SOCKS5握手异常: {str(e)}") + return False + + def _connect_to_target(self): + """连接到目标服务器""" + try: + if len(self.dest_addr) == 4: + dest_ip = '.'.join(str(b) for b in self.dest_addr) + else: + dest_ip = self.dest_addr.decode('utf-8', errors='ignore') + + # Python 3中bytes索引直接返回int,不需要ord() + if isinstance(self.dest_port, bytes): + dest_port = (self.dest_port[0] << 8) + self.dest_port[1] + else: + dest_port = (ord(self.dest_port[0]) << 8) + ord(self.dest_port[1]) + + proxy_type_map = { + 'socks4': socks.SOCKS4, + 'socks5': socks.SOCKS5, + 'http': socks.HTTP, + 'https': socks.HTTP + } + + proxy_type = proxy_type_map.get(self.upstream_proxy.protocol.value, socks.SOCKS5) + + target_socket = socks.socksocket() + target_socket.set_proxy( + proxy_type, + self.upstream_proxy.ip_address, + self.upstream_proxy.port, + username=self.upstream_proxy.username if self.upstream_proxy.username != "no need" else None, + password=self.upstream_proxy.password if self.upstream_proxy.password != "no need" else None + ) + + target_socket.settimeout(10) + target_socket.connect((dest_ip, dest_port)) + target_socket.settimeout(None) + + return target_socket + + except Exception as e: + self.logger.error(f"连接目标失败: {str(e)}") + return None + + def _forward_data(self, target_socket): + """双向转发数据""" + import select + + sockets = [self.client_socket, target_socket] + + while self.running: + try: + readable, _, _ = select.select(sockets, [], [], 1.0) + + for sock in readable: + if sock == self.client_socket: + data = self.client_socket.recv(4096) + if not data: + return + target_socket.sendall(data) + elif sock == target_socket: + data = target_socket.recv(4096) + if not data: + return + self.client_socket.sendall(data) + + except Exception as e: + self.logger.debug(f"转发数据时出错: {str(e)}") + break diff --git a/diagnose.py b/diagnose.py new file mode 100644 index 0000000..f8f6427 --- /dev/null +++ b/diagnose.py @@ -0,0 +1,124 @@ +"""诊断脚本 - 检查代理检测失败原因""" +import sys +import socket +import requests +from loguru import logger +from core.models import ProxyInfo, ProxyProtocol + + +def test_single_proxy(): + """测试单个代理的详细检测过程""" + logger.info("=" * 60) + logger.info("代理诊断工具") + logger.info("=" * 60) + + # 创建一个测试代理(使用local.json中的第一个) + proxy = ProxyInfo( + ip_address="171.6.75.111", + port=8080, + username="no need", + password="no need", + protocol=ProxyProtocol.SOCKS4 + ) + + logger.info(f"\n测试代理: {proxy.get_address()} ({proxy.protocol.value})\n") + + # 测试1: TCP连接 + logger.info("[测试1] TCP连接测试...") + try: + start_time = __import__('time').time() + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.settimeout(5) + result = sock.connect_ex((proxy.ip_address, proxy.port)) + elapsed = (__import__('time').time() - start_time) * 1000 + sock.close() + + if result == 0: + logger.info(f"✓ TCP连接成功,延迟: {elapsed:.0f}ms") + else: + logger.error(f"✗ TCP连接失败,错误码: {result}") + return False + except Exception as e: + logger.error(f"✗ TCP连接异常: {str(e)}") + return False + + # 测试2: SOCKS握手 + logger.info("\n[测试2] SOCKS握手测试...") + try: + import socks + + proxy_type = socks.SOCKS4 if proxy.protocol.value == 'socks4' else socks.SOCKS5 + + test_sock = socks.socksocket() + test_sock.set_proxy( + proxy_type, + proxy.ip_address, + proxy.port, + username=None, + password=None + ) + test_sock.settimeout(5) + + test_sock.connect(('www.google.com', 80)) + test_sock.close() + + logger.info("✓ SOCKS握手成功") + + except Exception as e: + logger.error(f"✗ SOCKS握手失败: {str(e)}") + return False + + # 测试3: Google连通性 + logger.info("\n[测试3] Google连通性测试...") + try: + proxies_dict = {} + protocol = proxy.protocol.value + + if protocol in ['socks4', 'socks5']: + proxies_dict['http'] = f"socks5://{proxy.get_address()}" + proxies_dict['https'] = f"socks5://{proxy.get_address()}" + + response = requests.get( + 'https://www.google.com/', + proxies=proxies_dict, + timeout=10, + verify=False + ) + + logger.info(f"✓ Google连通性测试成功,状态码: {response.status_code}") + + except Exception as e: + logger.error(f"✗ Google连通性测试失败: {str(e)}") + return False + + logger.info("\n" + "=" * 60) + logger.info("✓ 所有测试通过!该代理可用") + logger.info("=" * 60) + + return True + + +def check_network(): + """检查本地网络环境""" + logger.info("\n[网络检查] 本地网络环境...") + + # 测试直连Google + try: + response = requests.get('https://www.google.com/', timeout=5) + logger.info(f"✓ 直连Google成功,状态码: {response.status_code}") + logger.info(" 提示: 您的网络可以直接访问Google,可能不需要代理") + except Exception as e: + logger.info(f"✗ 直连Google失败: {str(e)}") + logger.info(" 提示: 您的网络无法直接访问Google,需要代理") + + +if __name__ == '__main__': + # 设置日志 + logger.remove() + logger.add(sys.stderr, level='INFO') + + # 检查网络 + check_network() + + # 测试代理 + test_single_proxy() diff --git a/gui/__init__.py b/gui/__init__.py new file mode 100644 index 0000000..c8dc43f --- /dev/null +++ b/gui/__init__.py @@ -0,0 +1,4 @@ +"""GUI模块""" +from .main_window import MainWindow + +__all__ = ['MainWindow'] diff --git a/gui/__pycache__/__init__.cpython-312.pyc b/gui/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..caca31a Binary files /dev/null and b/gui/__pycache__/__init__.cpython-312.pyc differ diff --git a/gui/__pycache__/main_window.cpython-312.pyc b/gui/__pycache__/main_window.cpython-312.pyc new file mode 100644 index 0000000..d27fba3 Binary files /dev/null and b/gui/__pycache__/main_window.cpython-312.pyc differ diff --git a/gui/main_window.py b/gui/main_window.py new file mode 100644 index 0000000..3e360aa --- /dev/null +++ b/gui/main_window.py @@ -0,0 +1,587 @@ +"""主窗口 - PyQt6 GUI界面""" +import sys +from PyQt6.QtWidgets import ( + QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, + QPushButton, QLabel, QTableWidget, QTableWidgetItem, + QTextEdit, QGroupBox, QFormLayout, QLineEdit, + QSpinBox, QComboBox, QCheckBox, QMessageBox, + QHeaderView, QProgressBar, QTabWidget, QDialog, + QDialogButtonBox, QFileDialog +) +from PyQt6.QtCore import Qt, QTimer, pyqtSignal, QThread +from PyQt6.QtGui import QFont, QColor +from loguru import logger +import yaml +import os + +from core import ProxyManager +from core.models import ProxyInfo, ProxyStatus + + +class WorkerThread(QThread): + """工作线程 - 处理耗时操作""" + progress_signal = pyqtSignal(str) + finished_signal = pyqtSignal(bool, str) + + def __init__(self, task_func, *args, **kwargs): + super().__init__() + self.task_func = task_func + self.args = args + self.kwargs = kwargs + + def run(self): + try: + result = self.task_func(*self.args, **self.kwargs) + self.finished_signal.emit(True, str(result)) + except Exception as e: + self.finished_signal.emit(False, str(e)) + + +class ConfigDialog(QDialog): + """配置对话框""" + + def __init__(self, config: dict, parent=None): + super().__init__(parent) + self.config = config.copy() + self.setWindowTitle("配置设置") + self.setFixedSize(600, 500) + self.init_ui() + + def init_ui(self): + layout = QVBoxLayout(self) + + # 代理源设置 + source_group = QGroupBox("代理源设置") + source_layout = QFormLayout() + + self.url_edit = QLineEdit(self.config.get('proxy_sources', {}).get('auto_fetch', {}).get('url', '')) + source_layout.addRow("自动获取URL:", self.url_edit) + + self.pages_spin = QSpinBox() + self.pages_spin.setRange(1, 20) + self.pages_spin.setValue(self.config.get('proxy_sources', {}).get('auto_fetch', {}).get('pages', 3)) + source_layout.addRow("抓取页数:", self.pages_spin) + + self.refresh_interval_spin = QSpinBox() + self.refresh_interval_spin.setRange(1, 60) + self.refresh_interval_spin.setValue(self.config.get('proxy_sources', {}).get('auto_fetch', {}).get('refresh_interval', 10)) + source_layout.addRow("刷新间隔(分钟):", self.refresh_interval_spin) + + self.local_file_checkbox = QCheckBox("启用本地文件") + self.local_file_checkbox.setChecked(self.config.get('proxy_sources', {}).get('local_file', {}).get('enabled', True)) + source_layout.addRow("", self.local_file_checkbox) + + source_group.setLayout(source_layout) + layout.addWidget(source_group) + + # 轮转策略 + rotation_group = QGroupBox("轮转策略") + rotation_layout = QFormLayout() + + self.mode_combo = QComboBox() + self.mode_combo.addItems(["manual", "auto"]) + self.mode_combo.setCurrentText(self.config.get('rotation', {}).get('mode', 'manual')) + rotation_layout.addRow("切换模式:", self.mode_combo) + + self.switch_interval_spin = QSpinBox() + self.switch_interval_spin.setRange(30, 3600) + self.switch_interval_spin.setValue(self.config.get('rotation', {}).get('auto_switch_interval', 300)) + rotation_layout.addRow("自动切换间隔(秒):", self.switch_interval_spin) + + self.latency_threshold_spin = QSpinBox() + self.latency_threshold_spin.setRange(100, 2000) + self.latency_threshold_spin.setValue(self.config.get('rotation', {}).get('latency_threshold', 500)) + rotation_layout.addRow("延迟阈值(ms):", self.latency_threshold_spin) + + rotation_group.setLayout(rotation_layout) + layout.addWidget(rotation_group) + + # 输出设置 + output_group = QGroupBox("输出设置") + output_layout = QFormLayout() + + self.host_edit = QLineEdit(self.config.get('output', {}).get('host', '127.0.0.1')) + output_layout.addRow("监听地址:", self.host_edit) + + self.port_spin = QSpinBox() + self.port_spin.setRange(1024, 65535) + self.port_spin.setValue(self.config.get('output', {}).get('port', 8745)) + output_layout.addRow("监听端口:", self.port_spin) + + output_group.setLayout(output_layout) + layout.addWidget(output_group) + + # 按钮 + button_box = QDialogButtonBox( + QDialogButtonBox.StandardButton.Ok | + QDialogButtonBox.StandardButton.Cancel + ) + button_box.accepted.connect(self.accept) + button_box.rejected.connect(self.reject) + layout.addWidget(button_box) + + def get_config(self) -> dict: + """获取配置""" + return { + 'proxy_sources': { + 'auto_fetch': { + 'enabled': True, + 'url': self.url_edit.text(), + 'pages': self.pages_spin.value(), + 'refresh_interval': self.refresh_interval_spin.value(), + 'output_file': 'proxy.json' + }, + 'local_file': { + 'enabled': self.local_file_checkbox.isChecked(), + 'path': 'local.json' + } + }, + 'rotation': { + 'mode': self.mode_combo.currentText(), + 'auto_switch_interval': self.switch_interval_spin.value(), + 'latency_threshold': self.latency_threshold_spin.value() + }, + 'output': { + 'host': self.host_edit.text(), + 'port': self.port_spin.value() + } + } + + +class MainWindow(QMainWindow): + """主窗口""" + + def __init__(self, config_path='config.yaml'): + super().__init__() + self.config_path = config_path + self.config = self.load_config() + self.proxy_manager = None + self.worker_thread = None + + self.setWindowTitle("WhereAmI - 多协议轮转代理工具") + self.resize(1200, 800) + + self.init_ui() + self.init_proxy_manager() + + def load_config(self) -> dict: + """加载配置文件""" + if os.path.exists(self.config_path): + with open(self.config_path, 'r', encoding='utf-8') as f: + return yaml.safe_load(f) + return {} + + def save_config(self): + """保存配置文件""" + with open(self.config_path, 'w', encoding='utf-8') as f: + yaml.dump(self.config, f, allow_unicode=True, default_flow_style=False) + + def init_ui(self): + """初始化UI""" + central_widget = QWidget() + self.setCentralWidget(central_widget) + main_layout = QVBoxLayout(central_widget) + + # 顶部状态栏 + status_layout = QHBoxLayout() + + self.status_label = QLabel("状态: 未启动") + self.status_label.setStyleSheet("font-weight: bold; color: red;") + status_layout.addWidget(self.status_label) + + self.active_proxy_label = QLabel("当前代理: 无") + status_layout.addWidget(self.active_proxy_label) + + self.stats_label = QLabel("统计: 0/0") + status_layout.addWidget(self.stats_label) + + status_layout.addStretch() + main_layout.addLayout(status_layout) + + # 控制按钮 + control_layout = QHBoxLayout() + + self.start_button = QPushButton("▶ 开始服务") + self.start_button.clicked.connect(self.on_start_service) + control_layout.addWidget(self.start_button) + + self.stop_button = QPushButton("⏹ 停止服务") + self.stop_button.clicked.connect(self.on_stop_service) + self.stop_button.setEnabled(False) + control_layout.addWidget(self.stop_button) + + self.switch_button = QPushButton("🔄 切换下一个") + self.switch_button.clicked.connect(self.on_switch_proxy) + self.switch_button.setEnabled(False) + control_layout.addWidget(self.switch_button) + + self.fetch_button = QPushButton("🌐 获取免费代理") + self.fetch_button.clicked.connect(self.on_fetch_proxies) + control_layout.addWidget(self.fetch_button) + + self.use_local_button = QPushButton("📁 使用本地代理") + self.use_local_button.clicked.connect(self.on_use_local_proxies) + control_layout.addWidget(self.use_local_button) + + self.fetch_other_button = QPushButton("🔧 其他代理(预留)") + self.fetch_other_button.clicked.connect(self.on_fetch_other_proxies) + self.fetch_other_button.setEnabled(False) # 暂时禁用,预留功能 + control_layout.addWidget(self.fetch_other_button) + + self.config_button = QPushButton("⚙️ 配置") + self.config_button.clicked.connect(self.on_open_config) + control_layout.addWidget(self.config_button) + + main_layout.addLayout(control_layout) + + # 进度条 + self.progress_bar = QProgressBar() + self.progress_bar.setVisible(False) + main_layout.addWidget(self.progress_bar) + + # 标签页 + tab_widget = QTabWidget() + + # 代理列表标签页 + proxy_tab = QWidget() + proxy_layout = QVBoxLayout(proxy_tab) + + # 过滤选项 + filter_layout = QHBoxLayout() + self.show_unavailable_checkbox = QCheckBox("显示不可用代理") + self.show_unavailable_checkbox.setChecked(False) + self.show_unavailable_checkbox.stateChanged.connect(self.on_filter_changed) + filter_layout.addWidget(self.show_unavailable_checkbox) + filter_layout.addStretch() + proxy_layout.addLayout(filter_layout) + + self.proxy_table = QTableWidget() + self.proxy_table.setColumnCount(10) + self.proxy_table.setHorizontalHeaderLabels([ + "IP地址", "端口", "协议", "国家", "状态", + "延迟(ms)", "匿名级别", "速度", "运行时间", "最后更新" + ]) + self.proxy_table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch) + self.proxy_table.setAlternatingRowColors(True) + self.proxy_table.setSelectionBehavior(QTableWidget.SelectionBehavior.SelectRows) + self.proxy_table.setSelectionMode(QTableWidget.SelectionMode.SingleSelection) + self.proxy_table.cellClicked.connect(self.on_proxy_selected) + proxy_layout.addWidget(self.proxy_table) + + tab_widget.addTab(proxy_tab, "代理列表") + + # 连接日志标签页 + connection_log_tab = QWidget() + connection_log_layout = QVBoxLayout(connection_log_tab) + + self.connection_log_text = QTextEdit() + self.connection_log_text.setReadOnly(True) + self.connection_log_text.setFont(QFont("Consolas", 9)) + connection_log_layout.addWidget(self.connection_log_text) + + clear_connection_log_button = QPushButton("清空连接日志") + clear_connection_log_button.clicked.connect(self.connection_log_text.clear) + connection_log_layout.addWidget(clear_connection_log_button) + + tab_widget.addTab(connection_log_tab, "连接日志") + + # 日志标签页 + log_tab = QWidget() + log_layout = QVBoxLayout(log_tab) + + self.log_text = QTextEdit() + self.log_text.setReadOnly(True) + self.log_text.setFont(QFont("Consolas", 9)) + log_layout.addWidget(self.log_text) + + clear_log_button = QPushButton("清空日志") + clear_log_button.clicked.connect(self.log_text.clear) + log_layout.addWidget(clear_log_button) + + tab_widget.addTab(log_tab, "日志") + + main_layout.addWidget(tab_widget) + + def init_proxy_manager(self): + """初始化代理管理器""" + # 定义连接日志回调 + def log_connection(message: str): + from datetime import datetime + timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + self.connection_log_text.append(f"[{timestamp}] {message}") + + self.proxy_manager = ProxyManager(self.config, connection_log_callback=log_connection) + self.selected_proxy = None # 用户选择的代理 + self.log("代理管理器已初始化") + + def on_start_service(self): + """启动服务 - 仅启动端口监听,需要先选择代理""" + # 检查是否选择了代理 + if not self.selected_proxy: + QMessageBox.warning(self, "警告", "请先选择一个代理节点!\n\n点击代理列表中的某一行来选择代理。") + return + + self.start_button.setEnabled(False) + self.progress_bar.setVisible(True) + self.progress_bar.setRange(0, 0) + self.log(f"正在启动服务,使用代理: {self.selected_proxy.get_address()}") + + def start_task(): + # 只使用选中的代理 + self.proxy_manager.set_proxies([self.selected_proxy]) + return self.proxy_manager.start_service() + + self.worker_thread = WorkerThread(start_task) + self.worker_thread.finished_signal.connect(self.on_service_started) + self.worker_thread.start() + + def on_service_started(self, success: bool, message: str): + """服务启动完成""" + self.progress_bar.setVisible(False) + self.start_button.setEnabled(True) + + if success: + self.status_label.setText("状态: 运行中") + self.status_label.setStyleSheet("font-weight: bold; color: green;") + self.stop_button.setEnabled(True) + self.switch_button.setEnabled(True) + self.log("服务启动成功") + self.update_proxy_table() + self.update_stats() + + # 定时更新状态 + self.update_timer = QTimer() + self.update_timer.timeout.connect(self.update_status) + self.update_timer.start(5000) + else: + self.log(f"服务启动失败: {message}") + QMessageBox.warning(self, "错误", f"启动服务失败:\n{message}") + + def on_stop_service(self): + """停止服务""" + self.log("正在停止服务...") + self.proxy_manager.stop_service() + + self.status_label.setText("状态: 已停止") + self.status_label.setStyleSheet("font-weight: bold; color: red;") + self.start_button.setEnabled(True) + self.stop_button.setEnabled(False) + self.switch_button.setEnabled(False) + + if hasattr(self, 'update_timer'): + self.update_timer.stop() + + self.log("服务已停止") + + def on_switch_proxy(self): + """切换代理""" + self.log("正在切换代理...") + + def switch_task(): + return self.proxy_manager.switch_to_next_proxy() + + self.worker_thread = WorkerThread(switch_task) + self.worker_thread.finished_signal.connect(self.on_proxy_switched) + self.worker_thread.start() + + def on_proxy_switched(self, success: bool, message: str): + """代理切换完成""" + if success: + self.log("代理切换成功") + self.update_status() + self.update_proxy_table() + else: + self.log(f"代理切换失败: {message}") + QMessageBox.warning(self, "警告", f"切换代理失败:\n{message}") + + def on_fetch_proxies(self): + """获取代理 - 保留旧方法以兼容""" + self.log("正在从网页获取代理...") + self.progress_bar.setVisible(True) + self.progress_bar.setRange(0, 0) + + def fetch_task(): + count = self.proxy_manager.load_proxies() + self.proxy_manager.check_all_proxies() + return count + + self.worker_thread = WorkerThread(fetch_task) + self.worker_thread.finished_signal.connect(self.on_fetch_completed) + self.worker_thread.start() + + def on_use_local_proxies(self): + """使用本地代理""" + self.log("正在加载本地代理...") + self.progress_bar.setVisible(True) + self.progress_bar.setRange(0, 0) + + def load_local_task(): + # 只加载本地文件 + config_backup = self.proxy_manager.config.copy() + self.proxy_manager.config['proxy_sources']['auto_fetch']['enabled'] = False + self.proxy_manager.config['proxy_sources']['local_file']['enabled'] = True + + count = self.proxy_manager.load_proxies() + self.proxy_manager.check_all_proxies() + + # 恢复配置 + self.proxy_manager.config = config_backup + return count + + self.worker_thread = WorkerThread(load_local_task) + self.worker_thread.finished_signal.connect(self.on_fetch_completed) + self.worker_thread.start() + + def on_fetch_other_proxies(self): + """获取其他代理(预留功能 - 后续用于启动 scratch.py 测试)""" + self.log("⚠️ 此功能暂未实现,预留用于后续测试") + QMessageBox.information(self, "提示", + "此功能暂未实现。\n\n" + "计划用途:启动 scratch.py 进行代理测试\n" + "后续将根据需求完善此功能。") + + def on_fetch_completed(self, success: bool, message: str): + """获取代理完成""" + self.progress_bar.setVisible(False) + + if success: + self.log(f"成功获取 {message} 个代理") + self.update_proxy_table() + self.update_stats() + else: + self.log(f"获取代理失败: {message}") + QMessageBox.warning(self, "错误", f"获取代理失败:\n{message}") + + def on_open_config(self): + """打开配置对话框""" + dialog = ConfigDialog(self.config, self) + if dialog.exec() == QDialog.DialogCode.Accepted: + self.config = dialog.get_config() + self.save_config() + self.log("配置已保存") + + def on_proxy_selected(self, row: int, column: int): + """用户选择代理""" + proxies = self.get_filtered_proxies() + if row < len(proxies): + self.selected_proxy = proxies[row] + self.active_proxy_label.setText( + f"选中代理: {self.selected_proxy.ip_address}:{self.selected_proxy.port} " + f"({self.selected_proxy.protocol.value}) - {self.selected_proxy.latency_ms:.0f}ms" + ) + self.log(f"已选择代理: {self.selected_proxy.get_address()}") + + def on_filter_changed(self, state): + """过滤条件改变""" + self.update_proxy_table() + + def get_filtered_proxies(self): + """获取过滤后的代理列表""" + if not self.proxy_manager: + return [] + + proxies = self.proxy_manager.proxies + + # 如果不显示不可用代理,则过滤掉 + if not self.show_unavailable_checkbox.isChecked(): + from core.models import ProxyStatus + proxies = [p for p in proxies if p.status != ProxyStatus.UNAVAILABLE] + + return proxies + + def update_status(self): + """更新状态显示""" + active_proxy = self.proxy_manager.get_active_proxy() + if active_proxy: + self.active_proxy_label.setText( + f"当前代理: {active_proxy.ip_address}:{active_proxy.port} " + f"({active_proxy.protocol.value}) - {active_proxy.latency_ms:.0f}ms" + ) + else: + self.active_proxy_label.setText("当前代理: 无") + + self.update_stats() + + def update_stats(self): + """更新统计信息""" + stats = self.proxy_manager.get_statistics() + self.stats_label.setText( + f"统计: 总计{stats['total']} | " + f"可用{stats['available']} | " + f"优秀{stats['excellent']} | " + f"不可用{stats['unavailable']}" + ) + + def update_proxy_table(self): + """更新代理列表表格""" + proxies = self.get_filtered_proxies() + + self.proxy_table.setRowCount(len(proxies)) + + for row, proxy in enumerate(proxies): + # IP地址 + self.proxy_table.setItem(row, 0, QTableWidgetItem(proxy.ip_address)) + + # 端口 + self.proxy_table.setItem(row, 1, QTableWidgetItem(str(proxy.port))) + + # 协议 + self.proxy_table.setItem(row, 2, QTableWidgetItem(proxy.protocol.value)) + + # 国家 + self.proxy_table.setItem(row, 3, QTableWidgetItem(proxy.country or "-")) + + # 状态 + status_item = QTableWidgetItem(self.get_status_text(proxy.status)) + status_item.setForeground(self.get_status_color(proxy.status)) + self.proxy_table.setItem(row, 4, status_item) + + # 延迟 + latency_text = f"{proxy.latency_ms:.0f}" if proxy.latency_ms < 9999 else "-" + self.proxy_table.setItem(row, 5, QTableWidgetItem(latency_text)) + + # 匿名级别 + self.proxy_table.setItem(row, 6, QTableWidgetItem(proxy.anonymity or "-")) + + # 速度 + self.proxy_table.setItem(row, 7, QTableWidgetItem(proxy.speed or "-")) + + # 运行时间 + self.proxy_table.setItem(row, 8, QTableWidgetItem(proxy.uptime_percentage or "-")) + + # 最后更新 + self.proxy_table.setItem(row, 9, QTableWidgetItem(proxy.last_updated or "-")) + + def get_status_text(self, status: ProxyStatus) -> str: + """获取状态文本""" + status_map = { + ProxyStatus.UNKNOWN: "未知", + ProxyStatus.CHECKING: "检测中", + ProxyStatus.AVAILABLE: "可用", + ProxyStatus.EXCELLENT: "优秀", + ProxyStatus.UNAVAILABLE: "不可用" + } + return status_map.get(status, "未知") + + def get_status_color(self, status: ProxyStatus) -> QColor: + """获取状态颜色""" + color_map = { + ProxyStatus.UNKNOWN: QColor("gray"), + ProxyStatus.CHECKING: QColor("orange"), + ProxyStatus.AVAILABLE: QColor("green"), + ProxyStatus.EXCELLENT: QColor("darkgreen"), + ProxyStatus.UNAVAILABLE: QColor("red") + } + return color_map.get(status, QColor("gray")) + + def log(self, message: str): + """添加日志""" + from datetime import datetime + timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + self.log_text.append(f"[{timestamp}] {message}") + + def closeEvent(self, event): + """关闭事件""" + if self.proxy_manager: + self.proxy_manager.stop_service() + event.accept() diff --git a/local.json b/local.json new file mode 100644 index 0000000..60f1e94 --- /dev/null +++ b/local.json @@ -0,0 +1,34 @@ +[ + { + "ip_address": "127.0.0.1", + "port": 1080, + "username": "no need", + "password": "no need", + "protocol": "socks5", + "country": "HK", + "anonymity": "", + "speed": "", + "uptime_percentage": "", + "response_time": "", + "latency": "0ms", + "last_updated": "", + "status": "excellent", + "last_checked": "2026-06-15T00:36:54.926006" + }, + { + "ip_address": "127.0.0.1", + "port": 10808, + "username": "no need", + "password": "no need", + "protocol": "socks5", + "country": "CN", + "anonymity": "", + "speed": "", + "uptime_percentage": "", + "response_time": "", + "latency": "1ms", + "last_updated": "", + "status": "excellent", + "last_checked": "2026-06-15T00:36:54.926006" + } +] \ No newline at end of file diff --git a/logs/whereami.log b/logs/whereami.log new file mode 100644 index 0000000..960c5c1 --- /dev/null +++ b/logs/whereami.log @@ -0,0 +1,307 @@ +2026-06-14 21:52:14.830 | INFO | __main__:main:39 - ============================================================ +2026-06-14 21:52:14.830 | INFO | __main__:main:40 - WhereAmI - 多协议轮转代理工具 +2026-06-14 21:52:14.830 | INFO | __main__:main:41 - ============================================================ +2026-06-14 21:52:15.295 | INFO | __main__:main:54 - GUI界面已启动 +2026-06-14 22:00:13.698 | INFO | core.data_source:fetch_from_web:21 - 开始从网页获取代理: http://cn.freevpnnode.com/free-proxy-for-thailand/ +2026-06-14 22:00:17.015 | INFO | core.data_source:fetch_from_web:36 - 成功获取 30 个代理 +2026-06-14 22:00:17.017 | INFO | core.data_source:load_from_file:145 - 从本地文件加载代理: local.json +2026-06-14 22:00:17.017 | INFO | core.data_source:load_from_file:165 - 成功加载 3 个代理 +2026-06-14 22:00:17.017 | INFO | core.proxy_manager:load_proxies:60 - 加载了 30 个唯一代理 +2026-06-14 22:00:17.017 | INFO | core.health_checker:batch_check:107 - 开始批量检测 30 个代理 +2026-06-14 22:00:32.020 | INFO | core.health_checker:batch_check:131 - 完成检测,共检测 30 个代理 +2026-06-14 22:00:32.020 | INFO | core.proxy_manager:check_all_proxies:82 - 检测到 0 个可用代理 +2026-06-14 22:00:52.573 | INFO | core.data_source:fetch_from_web:21 - 开始从网页获取代理: http://cn.freevpnnode.com/free-proxy-for-thailand/ +2026-06-14 22:00:54.455 | INFO | core.data_source:fetch_from_web:36 - 成功获取 30 个代理 +2026-06-14 22:00:54.456 | INFO | core.data_source:load_from_file:145 - 从本地文件加载代理: local.json +2026-06-14 22:00:54.456 | INFO | core.data_source:load_from_file:165 - 成功加载 3 个代理 +2026-06-14 22:00:54.456 | INFO | core.proxy_manager:load_proxies:60 - 加载了 30 个唯一代理 +2026-06-14 22:00:54.456 | INFO | core.health_checker:batch_check:107 - 开始批量检测 30 个代理 +2026-06-14 22:01:09.458 | INFO | core.health_checker:batch_check:131 - 完成检测,共检测 30 个代理 +2026-06-14 22:01:09.458 | INFO | core.proxy_manager:check_all_proxies:82 - 检测到 0 个可用代理 +2026-06-14 22:01:09.458 | ERROR | core.proxy_manager:start_service:99 - 没有可用的代理,无法启动服务 +2026-06-14 22:07:55.369 | WARNING | core.proxy_manager:switch_to_next_proxy:129 - 服务未运行 +2026-06-14 22:43:07.770 | INFO | __main__:main:39 - ============================================================ +2026-06-14 22:43:07.770 | INFO | __main__:main:40 - WhereAmI - 多协议轮转代理工具 +2026-06-14 22:43:07.770 | INFO | __main__:main:41 - ============================================================ +2026-06-14 22:43:08.153 | INFO | __main__:main:54 - GUI界面已启动 +2026-06-14 22:44:15.007 | INFO | core.data_source:fetch_from_web:21 - 开始从网页获取代理: http://cn.freevpnnode.com/free-proxy-for-thailand/ +2026-06-14 22:44:17.287 | INFO | core.data_source:fetch_from_web:36 - 成功获取 30 个代理 +2026-06-14 22:44:17.289 | INFO | core.data_source:save_to_file:183 - 保存 30 个代理到文件: proxy.json +2026-06-14 22:44:17.289 | INFO | core.data_source:fetch_from_web:41 - 代理已保存到: proxy.json +2026-06-14 22:44:17.290 | INFO | core.data_source:load_from_file:150 - 从本地文件加载代理: local.json +2026-06-14 22:44:17.290 | INFO | core.data_source:load_from_file:170 - 成功加载 1 个代理 +2026-06-14 22:44:17.290 | INFO | core.proxy_manager:load_proxies:61 - 加载了 31 个唯一代理 +2026-06-14 22:44:17.290 | INFO | core.health_checker:batch_check:107 - 开始批量检测 31 个代理 +2026-06-14 22:44:32.294 | INFO | core.health_checker:batch_check:131 - 完成检测,共检测 31 个代理 +2026-06-14 22:44:32.294 | INFO | core.proxy_manager:check_all_proxies:83 - 检测到 1 个可用代理 +2026-06-14 22:44:32.294 | INFO | core.simple_proxy_forwarder:validate_proxies:40 - 开始验证 1 个代理 +2026-06-14 22:44:32.295 | INFO | core.simple_proxy_forwarder:validate_proxies:58 - 验证完成,1 个代理可用 +2026-06-14 22:44:32.295 | INFO | core.simple_proxy_forwarder:start:134 - 使用代理: 127.0.0.1:10808 (socks5) +2026-06-14 22:44:32.297 | INFO | core.simple_proxy_forwarder:_run_forwarder:152 - SOCKS5转发器已启动: 127.0.0.1:8745 +2026-06-14 22:44:32.297 | INFO | core.proxy_manager:start_service:109 - 代理服务已启动 +2026-06-14 22:51:37.619 | WARNING | core.proxy_manager:start_service:89 - 服务已在运行 +2026-06-14 22:53:02.658 | ERROR | core.simple_proxy_forwarder:_connect_to_target:341 - 连接目标失败: ord() expected string of length 1, but int found +2026-06-14 22:58:24.158 | INFO | core.simple_proxy_forwarder:stop:208 - 转发器已停止 +2026-06-14 22:58:24.158 | INFO | core.proxy_manager:stop_service:125 - 代理服务已停止 +2026-06-14 23:08:35.632 | INFO | __main__:main:39 - ============================================================ +2026-06-14 23:08:35.632 | INFO | __main__:main:40 - WhereAmI - 多协议轮转代理工具 +2026-06-14 23:08:35.632 | INFO | __main__:main:41 - ============================================================ +2026-06-14 23:08:35.957 | INFO | __main__:main:54 - GUI界面已启动 +2026-06-14 23:08:46.172 | INFO | core.data_source:fetch_from_web:21 - 开始从网页获取代理: http://cn.freevpnnode.com/free-proxy-for-thailand/ +2026-06-14 23:08:49.077 | INFO | core.data_source:fetch_from_web:36 - 成功获取 30 个代理 +2026-06-14 23:08:49.078 | INFO | core.data_source:save_to_file:183 - 保存 30 个代理到文件: proxy.json +2026-06-14 23:08:49.078 | INFO | core.data_source:fetch_from_web:41 - 代理已保存到: proxy.json +2026-06-14 23:08:49.079 | INFO | core.proxy_manager:load_proxies:61 - 加载了 30 个唯一代理 +2026-06-14 23:08:49.079 | INFO | core.health_checker:batch_check:107 - 开始批量检测 30 个代理 +2026-06-14 23:09:04.082 | INFO | core.health_checker:batch_check:131 - 完成检测,共检测 30 个代理 +2026-06-14 23:09:04.082 | INFO | core.proxy_manager:check_all_proxies:83 - 检测到 0 个可用代理 +2026-06-14 23:09:04.082 | ERROR | core.proxy_manager:start_service:100 - 没有可用的代理,无法启动服务 +2026-06-14 23:12:37.912 | WARNING | core.proxy_manager:switch_to_next_proxy:130 - 服务未运行 +2026-06-14 23:12:48.176 | INFO | core.data_source:fetch_from_web:21 - 开始从网页获取代理: http://cn.freevpnnode.com/free-proxy-for-thailand/ +2026-06-14 23:12:50.424 | INFO | core.data_source:fetch_from_web:36 - 成功获取 30 个代理 +2026-06-14 23:12:50.425 | INFO | core.data_source:save_to_file:183 - 保存 30 个代理到文件: proxy.json +2026-06-14 23:12:50.426 | INFO | core.data_source:fetch_from_web:41 - 代理已保存到: proxy.json +2026-06-14 23:12:50.426 | INFO | core.proxy_manager:load_proxies:61 - 加载了 30 个唯一代理 +2026-06-14 23:12:50.427 | INFO | core.health_checker:batch_check:107 - 开始批量检测 30 个代理 +2026-06-14 23:13:05.429 | INFO | core.health_checker:batch_check:131 - 完成检测,共检测 30 个代理 +2026-06-14 23:13:05.429 | INFO | core.proxy_manager:check_all_proxies:83 - 检测到 0 个可用代理 +2026-06-14 23:13:05.430 | ERROR | core.proxy_manager:start_service:100 - 没有可用的代理,无法启动服务 +2026-06-14 23:24:43.738 | INFO | __main__:main:39 - ============================================================ +2026-06-14 23:24:43.738 | INFO | __main__:main:40 - WhereAmI - 多协议轮转代理工具 +2026-06-14 23:24:43.738 | INFO | __main__:main:41 - ============================================================ +2026-06-14 23:24:44.131 | INFO | __main__:main:54 - GUI界面已启动 +2026-06-14 23:25:10.991 | INFO | core.data_source:load_from_file:150 - 从本地文件加载代理: local.json +2026-06-14 23:25:10.991 | INFO | core.data_source:load_from_file:170 - 成功加载 1 个代理 +2026-06-14 23:25:10.991 | INFO | core.proxy_manager:load_proxies:61 - 加载了 1 个唯一代理 +2026-06-14 23:25:10.992 | INFO | core.health_checker:batch_check:107 - 开始批量检测 1 个代理 +2026-06-14 23:25:12.901 | INFO | core.health_checker:batch_check:131 - 完成检测,共检测 1 个代理 +2026-06-14 23:25:12.901 | INFO | core.proxy_manager:check_all_proxies:89 - 检测到 1 个可用代理 +2026-06-14 23:25:18.678 | INFO | core.proxy_manager:set_proxies:69 - 设置代理列表: 1 个代理 +2026-06-14 23:25:18.679 | INFO | core.data_source:load_from_file:150 - 从本地文件加载代理: local.json +2026-06-14 23:25:18.679 | INFO | core.data_source:load_from_file:170 - 成功加载 1 个代理 +2026-06-14 23:25:18.679 | INFO | core.proxy_manager:load_proxies:61 - 加载了 1 个唯一代理 +2026-06-14 23:25:18.680 | INFO | core.health_checker:batch_check:107 - 开始批量检测 1 个代理 +2026-06-14 23:25:19.883 | INFO | core.health_checker:batch_check:131 - 完成检测,共检测 1 个代理 +2026-06-14 23:25:19.884 | INFO | core.proxy_manager:check_all_proxies:89 - 检测到 1 个可用代理 +2026-06-14 23:25:19.884 | INFO | core.simple_proxy_forwarder:validate_proxies:43 - 开始验证 1 个代理 +2026-06-14 23:25:19.886 | INFO | core.simple_proxy_forwarder:validate_proxies:61 - 验证完成,1 个代理可用 +2026-06-14 23:25:19.886 | INFO | core.simple_proxy_forwarder:start:137 - 使用代理: 127.0.0.1:10808 (socks5) +2026-06-14 23:25:19.886 | INFO | core.simple_proxy_forwarder:_run_forwarder:155 - SOCKS5转发器已启动: 127.0.0.1:8745 +2026-06-14 23:25:19.886 | INFO | core.proxy_manager:start_service:115 - 代理服务已启动 +2026-06-14 23:28:39.036 | INFO | core.simple_proxy_forwarder:stop:212 - 转发器已停止 +2026-06-14 23:28:39.036 | INFO | core.proxy_manager:stop_service:131 - 代理服务已停止 +2026-06-14 23:28:55.425 | INFO | __main__:main:39 - ============================================================ +2026-06-14 23:28:55.425 | INFO | __main__:main:40 - WhereAmI - 多协议轮转代理工具 +2026-06-14 23:28:55.425 | INFO | __main__:main:41 - ============================================================ +2026-06-14 23:28:55.843 | INFO | __main__:main:54 - GUI界面已启动 +2026-06-14 23:29:02.029 | INFO | core.data_source:load_from_file:150 - 从本地文件加载代理: local.json +2026-06-14 23:29:02.030 | INFO | core.data_source:load_from_file:170 - 成功加载 1 个代理 +2026-06-14 23:29:02.030 | INFO | core.proxy_manager:load_proxies:61 - 加载了 1 个唯一代理 +2026-06-14 23:29:02.030 | INFO | core.health_checker:batch_check:107 - 开始批量检测 1 个代理 +2026-06-14 23:29:02.817 | INFO | core.health_checker:batch_check:131 - 完成检测,共检测 1 个代理 +2026-06-14 23:29:02.818 | INFO | core.proxy_manager:check_all_proxies:89 - 检测到 1 个可用代理 +2026-06-14 23:29:10.572 | INFO | core.data_source:load_from_file:150 - 从本地文件加载代理: local.json +2026-06-14 23:29:10.573 | INFO | core.data_source:load_from_file:170 - 成功加载 1 个代理 +2026-06-14 23:29:10.573 | INFO | core.proxy_manager:load_proxies:61 - 加载了 1 个唯一代理 +2026-06-14 23:29:10.573 | INFO | core.health_checker:batch_check:107 - 开始批量检测 1 个代理 +2026-06-14 23:29:11.692 | INFO | core.health_checker:batch_check:131 - 完成检测,共检测 1 个代理 +2026-06-14 23:29:11.692 | INFO | core.proxy_manager:check_all_proxies:89 - 检测到 1 个可用代理 +2026-06-14 23:29:22.365 | INFO | core.data_source:load_from_file:150 - 从本地文件加载代理: local.json +2026-06-14 23:29:22.366 | INFO | core.data_source:load_from_file:170 - 成功加载 1 个代理 +2026-06-14 23:29:22.366 | INFO | core.proxy_manager:load_proxies:61 - 加载了 1 个唯一代理 +2026-06-14 23:29:22.367 | INFO | core.health_checker:batch_check:107 - 开始批量检测 1 个代理 +2026-06-14 23:29:23.242 | INFO | core.health_checker:batch_check:131 - 完成检测,共检测 1 个代理 +2026-06-14 23:29:23.243 | INFO | core.proxy_manager:check_all_proxies:89 - 检测到 1 个可用代理 +2026-06-14 23:42:59.749 | INFO | __main__:main:39 - ============================================================ +2026-06-14 23:42:59.749 | INFO | __main__:main:40 - WhereAmI - 多协议轮转代理工具 +2026-06-14 23:42:59.749 | INFO | __main__:main:41 - ============================================================ +2026-06-14 23:43:00.039 | INFO | __main__:main:54 - GUI界面已启动 +2026-06-14 23:43:04.843 | INFO | core.data_source:fetch_from_web:27 - 开始从网页获取代理: http://cn.freevpnnode.com/free-proxy-for-thailand (共3页) +2026-06-14 23:43:04.844 | INFO | core.data_source:fetch_from_web:43 - 正在获取第 1/3 页: http://cn.freevpnnode.com/free-proxy-for-thailand?page=1 +2026-06-14 23:43:07.102 | INFO | core.data_source:fetch_from_web:54 - 第1页成功获取 30 个代理 +2026-06-14 23:43:07.102 | INFO | core.data_source:fetch_from_web:43 - 正在获取第 2/3 页: http://cn.freevpnnode.com/free-proxy-for-thailand?page=2 +2026-06-14 23:43:10.670 | INFO | core.data_source:fetch_from_web:54 - 第2页成功获取 30 个代理 +2026-06-14 23:43:10.671 | INFO | core.data_source:fetch_from_web:43 - 正在获取第 3/3 页: http://cn.freevpnnode.com/free-proxy-for-thailand?page=3 +2026-06-14 23:43:12.852 | INFO | core.data_source:fetch_from_web:54 - 第3页成功获取 30 个代理 +2026-06-14 23:43:12.852 | INFO | core.data_source:fetch_from_web:70 - 成功获取 90 个唯一代理(共90个,去重后) +2026-06-14 23:43:12.854 | INFO | core.data_source:save_to_file:217 - 保存 90 个代理到文件: proxy.json +2026-06-14 23:43:12.854 | INFO | core.data_source:fetch_from_web:75 - 代理已保存到: proxy.json +2026-06-14 23:43:12.856 | INFO | core.data_source:load_from_file:184 - 从本地文件加载代理: local.json +2026-06-14 23:43:12.856 | INFO | core.data_source:load_from_file:204 - 成功加载 1 个代理 +2026-06-14 23:43:12.856 | INFO | core.proxy_manager:load_proxies:64 - 加载了 91 个唯一代理 +2026-06-14 23:43:12.856 | INFO | core.health_checker:batch_check:107 - 开始批量检测 91 个代理 +2026-06-14 23:43:54.264 | INFO | core.health_checker:batch_check:131 - 完成检测,共检测 91 个代理 +2026-06-14 23:43:54.266 | INFO | core.proxy_manager:_sync_latency_to_file:128 - 已同步 90 个代理的延迟信息到 proxy.json +2026-06-14 23:43:54.266 | INFO | core.proxy_manager:check_all_proxies:95 - 检测到 1 个可用代理 +2026-06-14 23:49:40.924 | INFO | __main__:main:39 - ============================================================ +2026-06-14 23:49:40.924 | INFO | __main__:main:40 - WhereAmI - 多协议轮转代理工具 +2026-06-14 23:49:40.925 | INFO | __main__:main:41 - ============================================================ +2026-06-14 23:49:41.295 | INFO | __main__:main:54 - GUI界面已启动 +2026-06-14 23:50:08.001 | INFO | core.data_source:fetch_from_web:27 - 开始从网页获取代理: http://cn.freevpnnode.com/free-proxy-for-thailand (共1页) +2026-06-14 23:50:08.002 | INFO | core.data_source:fetch_from_web:43 - 正在获取第 1/1 页: http://cn.freevpnnode.com/free-proxy-for-thailand?page=1 +2026-06-14 23:50:11.397 | INFO | core.data_source:fetch_from_web:54 - 第1页成功获取 30 个代理 +2026-06-14 23:50:11.397 | INFO | core.data_source:fetch_from_web:70 - 成功获取 30 个唯一代理(共30个,去重后) +2026-06-14 23:50:11.398 | INFO | core.data_source:save_to_file:217 - 保存 30 个代理到文件: proxy.json +2026-06-14 23:50:11.398 | INFO | core.data_source:fetch_from_web:75 - 代理已保存到: proxy.json +2026-06-14 23:50:11.400 | INFO | core.proxy_manager:load_proxies:64 - 加载了 30 个唯一代理 +2026-06-14 23:50:11.400 | INFO | core.health_checker:batch_check:121 - 开始批量检测 30 个代理 +2026-06-14 23:50:26.402 | INFO | core.health_checker:batch_check:145 - 完成检测,共检测 30 个代理 +2026-06-14 23:50:26.403 | INFO | core.proxy_manager:_sync_latency_to_file:128 - 已同步 30 个代理的延迟信息到 proxy.json +2026-06-14 23:50:26.404 | INFO | core.proxy_manager:check_all_proxies:95 - 检测到 0 个可用代理 +2026-06-14 23:50:58.207 | INFO | core.data_source:load_from_file:184 - 从本地文件加载代理: local.json +2026-06-14 23:50:58.208 | INFO | core.data_source:load_from_file:204 - 成功加载 1 个代理 +2026-06-14 23:50:58.208 | INFO | core.proxy_manager:load_proxies:64 - 加载了 1 个唯一代理 +2026-06-14 23:50:58.208 | INFO | core.health_checker:batch_check:121 - 开始批量检测 1 个代理 +2026-06-14 23:50:59.152 | INFO | core.health_checker:batch_check:145 - 完成检测,共检测 1 个代理 +2026-06-14 23:50:59.153 | INFO | core.proxy_manager:check_all_proxies:95 - 检测到 1 个可用代理 +2026-06-14 23:51:59.874 | INFO | core.data_source:load_from_file:184 - 从本地文件加载代理: local.json +2026-06-14 23:51:59.875 | INFO | core.data_source:load_from_file:204 - 成功加载 1 个代理 +2026-06-14 23:51:59.875 | INFO | core.proxy_manager:load_proxies:64 - 加载了 1 个唯一代理 +2026-06-14 23:51:59.875 | INFO | core.health_checker:batch_check:121 - 开始批量检测 1 个代理 +2026-06-14 23:52:00.629 | INFO | core.health_checker:batch_check:145 - 完成检测,共检测 1 个代理 +2026-06-14 23:52:00.630 | INFO | core.proxy_manager:check_all_proxies:95 - 检测到 1 个可用代理 +2026-06-14 23:52:15.656 | INFO | core.data_source:load_from_file:184 - 从本地文件加载代理: local.json +2026-06-14 23:52:15.657 | INFO | core.data_source:load_from_file:204 - 成功加载 1 个代理 +2026-06-14 23:52:15.657 | INFO | core.proxy_manager:load_proxies:64 - 加载了 1 个唯一代理 +2026-06-14 23:52:15.658 | INFO | core.health_checker:batch_check:121 - 开始批量检测 1 个代理 +2026-06-14 23:52:16.452 | INFO | core.health_checker:batch_check:145 - 完成检测,共检测 1 个代理 +2026-06-14 23:52:16.453 | INFO | core.proxy_manager:check_all_proxies:95 - 检测到 1 个可用代理 +2026-06-15 00:03:41.657 | INFO | __main__:main:39 - ============================================================ +2026-06-15 00:03:41.658 | INFO | __main__:main:40 - WhereAmI - 多协议轮转代理工具 +2026-06-15 00:03:41.658 | INFO | __main__:main:41 - ============================================================ +2026-06-15 00:03:42.135 | INFO | __main__:main:54 - GUI界面已启动 +2026-06-15 00:03:44.142 | INFO | core.data_source:load_from_file:184 - 从本地文件加载代理: local.json +2026-06-15 00:03:44.142 | INFO | core.data_source:load_from_file:204 - 成功加载 2 个代理 +2026-06-15 00:03:44.142 | INFO | core.proxy_manager:load_proxies:64 - 加载了 2 个唯一代理 +2026-06-15 00:03:44.143 | INFO | core.health_checker:batch_check:121 - 开始批量检测 2 个代理 +2026-06-15 00:03:49.157 | INFO | core.health_checker:batch_check:145 - 完成检测,共检测 2 个代理 +2026-06-15 00:03:49.158 | INFO | core.proxy_manager:_sync_to_single_file:154 - 已同步 2 个代理的延迟信息到 local.json +2026-06-15 00:03:49.159 | INFO | core.proxy_manager:check_all_proxies:95 - 检测到 1 个可用代理 +2026-06-15 00:05:16.491 | INFO | core.data_source:load_from_file:184 - 从本地文件加载代理: local.json +2026-06-15 00:05:16.492 | INFO | core.data_source:load_from_file:204 - 成功加载 2 个代理 +2026-06-15 00:05:16.492 | INFO | core.proxy_manager:load_proxies:64 - 加载了 2 个唯一代理 +2026-06-15 00:05:16.492 | INFO | core.health_checker:batch_check:121 - 开始批量检测 2 个代理 +2026-06-15 00:05:21.497 | INFO | core.health_checker:batch_check:145 - 完成检测,共检测 2 个代理 +2026-06-15 00:05:21.497 | INFO | core.proxy_manager:_sync_to_single_file:154 - 已同步 2 个代理的延迟信息到 local.json +2026-06-15 00:05:21.498 | INFO | core.proxy_manager:check_all_proxies:95 - 检测到 1 个可用代理 +2026-06-15 00:05:25.137 | INFO | core.proxy_manager:set_proxies:72 - 设置代理列表: 1 个代理 +2026-06-15 00:05:25.138 | INFO | core.data_source:load_from_file:184 - 从本地文件加载代理: local.json +2026-06-15 00:05:25.138 | INFO | core.data_source:load_from_file:204 - 成功加载 2 个代理 +2026-06-15 00:05:25.138 | INFO | core.proxy_manager:load_proxies:64 - 加载了 2 个唯一代理 +2026-06-15 00:05:25.138 | INFO | core.health_checker:batch_check:121 - 开始批量检测 2 个代理 +2026-06-15 00:05:30.141 | INFO | core.health_checker:batch_check:145 - 完成检测,共检测 2 个代理 +2026-06-15 00:05:30.142 | INFO | core.proxy_manager:_sync_to_single_file:154 - 已同步 2 个代理的延迟信息到 local.json +2026-06-15 00:05:30.142 | INFO | core.proxy_manager:check_all_proxies:95 - 检测到 1 个可用代理 +2026-06-15 00:05:30.142 | INFO | core.simple_proxy_forwarder:validate_proxies:43 - 开始验证 1 个代理 +2026-06-15 00:05:30.144 | INFO | core.simple_proxy_forwarder:validate_proxies:61 - 验证完成,1 个代理可用 +2026-06-15 00:05:30.144 | INFO | core.simple_proxy_forwarder:start:137 - 使用代理: 127.0.0.1:10808 (socks5) +2026-06-15 00:05:30.145 | INFO | core.simple_proxy_forwarder:_run_forwarder:155 - SOCKS5转发器已启动: 127.0.0.1:8745 +2026-06-15 00:05:30.145 | INFO | core.proxy_manager:start_service:182 - 代理服务已启动 +2026-06-15 00:06:07.982 | INFO | core.simple_proxy_forwarder:switch_proxy:222 - 切换到代理: 127.0.0.1:10808 +2026-06-15 00:06:07.982 | INFO | core.proxy_manager:switch_to_next_proxy:223 - 成功切换到下一个代理: 127.0.0.1:10808 +2026-06-15 00:06:27.214 | INFO | core.simple_proxy_forwarder:stop:212 - 转发器已停止 +2026-06-15 00:06:27.214 | INFO | core.proxy_manager:stop_service:198 - 代理服务已停止 +2026-06-15 00:06:41.226 | INFO | core.data_source:load_from_file:184 - 从本地文件加载代理: local.json +2026-06-15 00:06:41.227 | INFO | core.data_source:load_from_file:204 - 成功加载 2 个代理 +2026-06-15 00:06:41.227 | INFO | core.proxy_manager:load_proxies:64 - 加载了 2 个唯一代理 +2026-06-15 00:06:41.227 | INFO | core.health_checker:batch_check:121 - 开始批量检测 2 个代理 +2026-06-15 00:06:52.540 | INFO | __main__:main:39 - ============================================================ +2026-06-15 00:06:52.541 | INFO | __main__:main:40 - WhereAmI - 多协议轮转代理工具 +2026-06-15 00:06:52.541 | INFO | __main__:main:41 - ============================================================ +2026-06-15 00:06:52.935 | INFO | __main__:main:54 - GUI界面已启动 +2026-06-15 00:06:54.619 | INFO | core.data_source:fetch_from_web:27 - 开始从网页获取代理: http://cn.freevpnnode.com/free-proxy-for-thailand (共3页) +2026-06-15 00:06:54.619 | INFO | core.data_source:fetch_from_web:43 - 正在获取第 1/3 页: http://cn.freevpnnode.com/free-proxy-for-thailand?page=1 +2026-06-15 00:06:56.765 | INFO | core.data_source:fetch_from_web:54 - 第1页成功获取 30 个代理 +2026-06-15 00:06:56.766 | INFO | core.data_source:fetch_from_web:43 - 正在获取第 2/3 页: http://cn.freevpnnode.com/free-proxy-for-thailand?page=2 +2026-06-15 00:06:59.006 | INFO | core.data_source:fetch_from_web:54 - 第2页成功获取 30 个代理 +2026-06-15 00:06:59.006 | INFO | core.data_source:fetch_from_web:43 - 正在获取第 3/3 页: http://cn.freevpnnode.com/free-proxy-for-thailand?page=3 +2026-06-15 00:07:01.748 | INFO | core.data_source:fetch_from_web:54 - 第3页成功获取 30 个代理 +2026-06-15 00:07:01.749 | INFO | core.data_source:fetch_from_web:70 - 成功获取 90 个唯一代理(共90个,去重后) +2026-06-15 00:07:01.751 | INFO | core.data_source:save_to_file:217 - 保存 90 个代理到文件: proxy.json +2026-06-15 00:07:01.752 | INFO | core.data_source:fetch_from_web:75 - 代理已保存到: proxy.json +2026-06-15 00:07:01.753 | INFO | core.data_source:load_from_file:184 - 从本地文件加载代理: local.json +2026-06-15 00:07:01.753 | INFO | core.data_source:load_from_file:204 - 成功加载 2 个代理 +2026-06-15 00:07:01.753 | INFO | core.proxy_manager:load_proxies:64 - 加载了 92 个唯一代理 +2026-06-15 00:07:01.754 | INFO | core.health_checker:batch_check:121 - 开始批量检测 92 个代理 +2026-06-15 00:07:45.350 | INFO | core.health_checker:batch_check:145 - 完成检测,共检测 92 个代理 +2026-06-15 00:07:45.352 | INFO | core.proxy_manager:_sync_to_single_file:154 - 已同步 90 个代理的延迟信息到 proxy.json +2026-06-15 00:07:45.353 | INFO | core.proxy_manager:_sync_to_single_file:154 - 已同步 2 个代理的延迟信息到 local.json +2026-06-15 00:07:45.353 | INFO | core.proxy_manager:check_all_proxies:95 - 检测到 1 个可用代理 +2026-06-15 00:10:08.957 | INFO | core.data_source:fetch_from_web:27 - 开始从网页获取代理: http://cn.freevpnnode.com/free-proxy-for-thailand (共3页) +2026-06-15 00:10:08.957 | INFO | core.data_source:fetch_from_web:43 - 正在获取第 1/3 页: http://cn.freevpnnode.com/free-proxy-for-thailand?page=1 +2026-06-15 00:10:12.772 | INFO | core.data_source:fetch_from_web:54 - 第1页成功获取 30 个代理 +2026-06-15 00:10:12.772 | INFO | core.data_source:fetch_from_web:43 - 正在获取第 2/3 页: http://cn.freevpnnode.com/free-proxy-for-thailand?page=2 +2026-06-15 00:10:15.405 | INFO | core.data_source:fetch_from_web:54 - 第2页成功获取 30 个代理 +2026-06-15 00:10:15.406 | INFO | core.data_source:fetch_from_web:43 - 正在获取第 3/3 页: http://cn.freevpnnode.com/free-proxy-for-thailand?page=3 +2026-06-15 00:10:17.962 | INFO | core.data_source:fetch_from_web:54 - 第3页成功获取 30 个代理 +2026-06-15 00:10:17.962 | INFO | core.data_source:fetch_from_web:70 - 成功获取 90 个唯一代理(共90个,去重后) +2026-06-15 00:10:17.963 | INFO | core.data_source:save_to_file:217 - 保存 90 个代理到文件: proxy.json +2026-06-15 00:10:17.964 | INFO | core.data_source:fetch_from_web:75 - 代理已保存到: proxy.json +2026-06-15 00:10:17.965 | INFO | core.data_source:load_from_file:184 - 从本地文件加载代理: local.json +2026-06-15 00:10:17.965 | INFO | core.data_source:load_from_file:204 - 成功加载 2 个代理 +2026-06-15 00:10:17.966 | INFO | core.proxy_manager:load_proxies:64 - 加载了 92 个唯一代理 +2026-06-15 00:10:17.966 | INFO | core.health_checker:batch_check:121 - 开始批量检测 92 个代理 +2026-06-15 00:11:01.708 | INFO | core.health_checker:batch_check:145 - 完成检测,共检测 92 个代理 +2026-06-15 00:11:01.710 | INFO | core.proxy_manager:_sync_to_single_file:154 - 已同步 90 个代理的延迟信息到 proxy.json +2026-06-15 00:11:01.711 | INFO | core.proxy_manager:_sync_to_single_file:154 - 已同步 2 个代理的延迟信息到 local.json +2026-06-15 00:11:01.711 | INFO | core.proxy_manager:check_all_proxies:95 - 检测到 1 个可用代理 +2026-06-15 00:15:06.941 | INFO | core.data_source:load_from_file:184 - 从本地文件加载代理: local.json +2026-06-15 00:15:06.942 | INFO | core.data_source:load_from_file:204 - 成功加载 3 个代理 +2026-06-15 00:15:06.942 | INFO | core.proxy_manager:load_proxies:64 - 加载了 2 个唯一代理 +2026-06-15 00:15:06.942 | INFO | core.health_checker:batch_check:121 - 开始批量检测 2 个代理 +2026-06-15 00:15:11.956 | INFO | core.health_checker:batch_check:145 - 完成检测,共检测 2 个代理 +2026-06-15 00:15:11.957 | INFO | core.proxy_manager:_sync_to_single_file:154 - 已同步 2 个代理的延迟信息到 local.json +2026-06-15 00:15:11.957 | INFO | core.proxy_manager:check_all_proxies:95 - 检测到 1 个可用代理 +2026-06-15 00:16:21.371 | INFO | core.data_source:load_from_file:184 - 从本地文件加载代理: local.json +2026-06-15 00:16:21.371 | INFO | core.data_source:load_from_file:204 - 成功加载 2 个代理 +2026-06-15 00:16:21.372 | INFO | core.proxy_manager:load_proxies:64 - 加载了 2 个唯一代理 +2026-06-15 00:16:21.372 | INFO | core.health_checker:batch_check:121 - 开始批量检测 2 个代理 +2026-06-15 00:16:26.375 | INFO | core.health_checker:batch_check:145 - 完成检测,共检测 2 个代理 +2026-06-15 00:16:26.376 | INFO | core.proxy_manager:_sync_to_single_file:154 - 已同步 2 个代理的延迟信息到 local.json +2026-06-15 00:16:26.376 | INFO | core.proxy_manager:check_all_proxies:95 - 检测到 1 个可用代理 +2026-06-15 00:17:26.994 | INFO | core.data_source:load_from_file:184 - 从本地文件加载代理: local.json +2026-06-15 00:17:26.995 | INFO | core.data_source:load_from_file:204 - 成功加载 2 个代理 +2026-06-15 00:17:26.995 | INFO | core.proxy_manager:load_proxies:64 - 加载了 2 个唯一代理 +2026-06-15 00:17:26.995 | INFO | core.health_checker:batch_check:121 - 开始批量检测 2 个代理 +2026-06-15 00:17:32.001 | INFO | core.health_checker:batch_check:145 - 完成检测,共检测 2 个代理 +2026-06-15 00:17:32.002 | INFO | core.proxy_manager:_sync_to_single_file:154 - 已同步 2 个代理的延迟信息到 local.json +2026-06-15 00:17:32.002 | INFO | core.proxy_manager:check_all_proxies:95 - 检测到 1 个可用代理 +2026-06-15 00:34:23.972 | INFO | __main__:main:39 - ============================================================ +2026-06-15 00:34:23.972 | INFO | __main__:main:40 - WhereAmI - 多协议轮转代理工具 +2026-06-15 00:34:23.973 | INFO | __main__:main:41 - ============================================================ +2026-06-15 00:34:24.253 | INFO | __main__:main:54 - GUI界面已启动 +2026-06-15 00:34:28.490 | INFO | core.data_source:load_from_file:184 - 从本地文件加载代理: local.json +2026-06-15 00:34:28.491 | INFO | core.data_source:load_from_file:204 - 成功加载 2 个代理 +2026-06-15 00:34:28.491 | INFO | core.proxy_manager:load_proxies:64 - 加载了 2 个唯一代理 +2026-06-15 00:34:28.491 | INFO | core.health_checker:batch_check:163 - 开始批量检测 2 个代理 +2026-06-15 00:34:33.505 | INFO | core.health_checker:batch_check:187 - 完成检测,共检测 2 个代理 +2026-06-15 00:34:33.506 | INFO | core.proxy_manager:_sync_to_single_file:154 - 已同步 2 个代理的延迟信息到 local.json +2026-06-15 00:34:33.506 | INFO | core.proxy_manager:check_all_proxies:95 - 检测到 1 个可用代理 +2026-06-15 00:36:31.683 | INFO | __main__:main:39 - ============================================================ +2026-06-15 00:36:31.683 | INFO | __main__:main:40 - WhereAmI - 多协议轮转代理工具 +2026-06-15 00:36:31.683 | INFO | __main__:main:41 - ============================================================ +2026-06-15 00:36:32.045 | INFO | __main__:main:54 - GUI界面已启动 +2026-06-15 00:36:34.568 | INFO | core.data_source:load_from_file:184 - 从本地文件加载代理: local.json +2026-06-15 00:36:34.568 | INFO | core.data_source:load_from_file:204 - 成功加载 2 个代理 +2026-06-15 00:36:34.568 | INFO | core.proxy_manager:load_proxies:64 - 加载了 2 个唯一代理 +2026-06-15 00:36:34.569 | INFO | core.health_checker:batch_check:163 - 开始批量检测 2 个代理 +2026-06-15 00:36:35.070 | INFO | core.health_checker:batch_check:187 - 完成检测,共检测 2 个代理 +2026-06-15 00:36:35.071 | INFO | core.proxy_manager:_sync_to_single_file:154 - 已同步 2 个代理的延迟信息到 local.json +2026-06-15 00:36:35.071 | INFO | core.proxy_manager:check_all_proxies:95 - 检测到 2 个可用代理 +2026-06-15 00:36:54.470 | INFO | core.proxy_manager:set_proxies:72 - 设置代理列表: 1 个代理 +2026-06-15 00:36:54.471 | INFO | core.data_source:load_from_file:184 - 从本地文件加载代理: local.json +2026-06-15 00:36:54.471 | INFO | core.data_source:load_from_file:204 - 成功加载 2 个代理 +2026-06-15 00:36:54.471 | INFO | core.proxy_manager:load_proxies:64 - 加载了 2 个唯一代理 +2026-06-15 00:36:54.471 | INFO | core.health_checker:batch_check:163 - 开始批量检测 2 个代理 +2026-06-15 00:36:54.925 | INFO | core.health_checker:batch_check:187 - 完成检测,共检测 2 个代理 +2026-06-15 00:36:54.926 | INFO | core.proxy_manager:_sync_to_single_file:154 - 已同步 2 个代理的延迟信息到 local.json +2026-06-15 00:36:54.927 | INFO | core.proxy_manager:check_all_proxies:95 - 检测到 2 个可用代理 +2026-06-15 00:36:54.927 | INFO | core.simple_proxy_forwarder:validate_proxies:43 - 开始验证 2 个代理 +2026-06-15 00:36:55.149 | INFO | core.simple_proxy_forwarder:validate_proxies:61 - 验证完成,2 个代理可用 +2026-06-15 00:36:55.149 | INFO | core.simple_proxy_forwarder:start:137 - 使用代理: 127.0.0.1:10808 (socks5) +2026-06-15 00:36:55.149 | INFO | core.simple_proxy_forwarder:_run_forwarder:155 - SOCKS5转发器已启动: 127.0.0.1:8745 +2026-06-15 00:36:55.150 | INFO | core.proxy_manager:start_service:182 - 代理服务已启动 +2026-06-15 00:37:47.384 | INFO | core.simple_proxy_forwarder:switch_proxy:222 - 切换到代理: 127.0.0.1:1080 +2026-06-15 00:37:47.385 | INFO | core.proxy_manager:switch_to_next_proxy:223 - 成功切换到下一个代理: 127.0.0.1:1080 +2026-06-15 00:37:54.467 | INFO | core.simple_proxy_forwarder:stop:212 - 转发器已停止 +2026-06-15 00:37:54.467 | INFO | core.proxy_manager:stop_service:198 - 代理服务已停止 diff --git a/main.py b/main.py new file mode 100644 index 0000000..dd4c803 --- /dev/null +++ b/main.py @@ -0,0 +1,65 @@ +"""主程序入口""" +import sys +import os +from loguru import logger +from PyQt6.QtWidgets import QApplication +from gui import MainWindow + + +def setup_logging(): + """设置日志""" + # 创建logs目录 + os.makedirs('logs', exist_ok=True) + + # 配置loguru + logger.remove() # 移除默认处理器 + + # 文件日志 + logger.add( + 'logs/whereami.log', + rotation='10 MB', + retention='5 days', + encoding='utf-8', + level='INFO' + ) + + # 控制台日志 + logger.add( + sys.stderr, + level='INFO', + format='{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {name}:{function}:{line} - {message}' + ) + + +def main(): + """主函数""" + # 设置日志 + setup_logging() + + logger.info("=" * 60) + logger.info("WhereAmI - 多协议轮转代理工具") + logger.info("=" * 60) + + try: + # 创建应用 + app = QApplication(sys.argv) + + # 设置应用样式 + app.setStyle('Fusion') + + # 创建主窗口 + window = MainWindow('config.yaml') + window.show() + + logger.info("GUI界面已启动") + + # 运行应用 + sys.exit(app.exec()) + + except Exception as e: + logger.error(f"程序异常退出: {str(e)}", exc_info=True) + sys.exit(1) + + +if __name__ == '__main__': + main() diff --git a/old/old_design.png b/old/old_design.png new file mode 100644 index 0000000..3c777b4 Binary files /dev/null and b/old/old_design.png differ diff --git a/old/page.html b/old/page.html new file mode 100644 index 0000000..ab2f19b --- /dev/null +++ b/old/page.html @@ -0,0 +1,739 @@ + + + + + + + + + + 泰国免费代理服务器列表 | 实时更新高匿名代理 - Free VPN Node, 第1页 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+
+
+
+ +
+
+

免费泰国代理服务器汇总 - 高速、稳定、实时更新

+

想要访问泰国本地内容、绕过地域限制或增强网络匿名性?我们提供全球最大的免费泰国代理服务器列表之一,涵盖 HTTP(S)、SOCKS4 和 SOCKS5 协议,满足不同需求。

+

为什么选择我们的泰国代理?

+
    +
  1. 实时更新:我们的系统每 3 分钟检测一次,剔除失效代理,确保你获取的 IP 始终可用。
  2. +
  3. 严格筛选:每天自动验证超过 100 万个代理,只保留高速、稳定的服务器。
  4. +
  5. 广泛兼容:支持浏览器、爬虫脚本、数据分析工具等,轻松集成到你的工作流程中。
  6. +
+

如何高效使用?

+
    +
  • 按协议筛选:SOCKS5 适合高隐私需求,HTTP(S) 适合普通网页访问。
  • +
  • 检查延迟:免费代理速度差异大,建议测试多个 IP 选择最优连接。
  • +
  • 注意安全性:避免在免费代理上登录银行账户或传输敏感数据,以防信息泄露。
  • +
+

免费 vs. 付费代理

+

虽然我们的列表完全免费,但付费代理通常更稳定、速度更快。如果你需要长期稳定的泰国 IP,可以考虑低延迟的付费方案。

+

现在,试试这些最新可用的泰国代理,解锁地理限制内容吧!

+
+ +
+
2026.06.13分享SSR、V2Ray、Clash免费节点,包含美国、韩国、德国、日本、新加坡,免费节点仅供学习研究,请勿非法使用。 【查看详情】
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IP地址端口号用户名密码国家协议Anonymity速度运行时间回复延迟更新
171.6.75.1118080**** hidden text**** hidden text + + 泰国 + TH + socks4elite (HIA)1 ms98%4797 ms160 ms3 min
202.139.198.153030**** hidden text**** hidden text + + 泰国 + TH + socks4elite (HIA)1 ms100%1777 ms168 ms4 min
116.58.254.2018080**** hidden text**** hidden text + + 泰国 + TH + socks5elite (HIA)1 ms79%4311 ms168 ms8 d
118.173.233.394898**** hidden text**** hidden text + + 泰国 + TH + socks5elite (HIA)1 ms46%5202 ms169 ms3 min
118.174.152.1568080**** hidden text**** hidden text + + 泰国 + TH + socks4elite (HIA)1 ms68%4884 ms169 ms4 min
203.158.221.15280**** hidden text**** hidden text + + 泰国 + TH + socks4elite (HIA)1 ms100%3379 ms170 ms3 min
110.78.138.1258080**** hidden text**** hidden text + + 泰国 + TH + socks4elite (HIA)1 ms83%3898 ms170 ms3 min
61.7.138.2144145**** hidden text**** hidden text + + 泰国 + TH + socks4elite (HIA)1 ms87%3699 ms170 ms8 d
61.7.184.1304153**** hidden text**** hidden text + + 泰国 + TH + socks4elite (HIA)1 ms80%4076 ms170 ms1 min
101.51.88.2474145**** hidden text**** hidden text + + 泰国 + TH + socks4elite (HIA)1 ms65%3112 ms170 ms1 min
118.174.219.1652168**** hidden text**** hidden text + + 泰国 + TH + socks5elite (HIA)1 ms81%2113 ms171 ms6 min
110.78.149.2214145**** hidden text**** hidden text + + 泰国 + TH + socks4elite (HIA)1 ms76%4301 ms172 ms4 min
101.109.100.698080**** hidden text**** hidden text + + 泰国 + TH + socks4elite (HIA)1 ms46%1624 ms172 ms2 min
110.78.82.705678**** hidden text**** hidden text + + 泰国 + TH + socks4elite (HIA)1 ms93%3402 ms172 ms7 min
134.236.16.1258080**** hidden text**** hidden text + + 泰国 + TH + socks4elite (HIA)1 ms68%3804 ms172 ms2 min
1.2.212.354145**** hidden text**** hidden text + + 泰国 + TH + socks4elite (HIA)1 ms69%4428 ms172 ms3 min
110.78.149.1104145**** hidden text**** hidden text + + 泰国 + TH + socks4elite (HIA)1 ms74%4886 ms172 ms8 d
110.77.228.17680**** hidden text**** hidden text + + 泰国 + TH + socks4elite (HIA)1 ms13%2998 ms172 ms3 min
101.109.107.1448080**** hidden text**** hidden text + + 泰国 + TH + socks4elite (HIA)1 ms48%4099 ms172 ms8 min
118.174.162.128080**** hidden text**** hidden text + + 泰国 + TH + socks5elite (HIA)1 ms63%2384 ms172 ms7 min
61.7.184.2164153**** hidden text**** hidden text + + 泰国 + TH + socks4elite (HIA)1 ms79%4906 ms173 ms8 d
101.51.241.1264153**** hidden text**** hidden text + + 泰国 + TH + socks4elite (HIA)1 ms52%4505 ms173 ms8 min
119.59.101.11180**** hidden text**** hidden text + + 泰国 + TH + socks4elite (HIA)1 ms100%2280 ms173 ms8 d
1.179.148.331080**** hidden text**** hidden text + + 泰国 + TH + socks4elite (HIA)1 ms94%4387 ms173 ms2 min
223.205.189.1645678**** hidden text**** hidden text + + 泰国 + TH + socks4elite (HIA)1 ms97%4104 ms173 ms8 d
110.78.186.2204145**** hidden text**** hidden text + + 泰国 + TH + socks4elite (HIA)1 ms62%2796 ms173 ms8 d
101.109.48.548080**** hidden text**** hidden text + + 泰国 + TH + socks4elite (HIA)1 ms75%3782 ms173 ms2 min
171.6.78.1648080**** hidden text**** hidden text + + 泰国 + TH + socks4elite (HIA)1 ms100%5006 ms174 ms2 min
134.236.30.925678**** hidden text**** hidden text + + 泰国 + TH + socks4elite (HIA)1 ms59%3696 ms174 ms8 d
118.173.230.191081**** hidden text**** hidden text + + 泰国 + TH + socks5elite (HIA)1 ms99%3001 ms174 ms1 min
+
+
+

泰国代理服务器:解锁数字边界的智能钥匙

+

核心价值解析:为什么选择泰国代理?

+

在全球化互联网时代,地理限制成为了数字世界中的无形围墙。泰国代理服务器恰如一把智能钥匙,为您打开这扇限制之门。通过隐藏真实IP地址,它不仅能提升您的网络匿名性,更为关键业务场景提供支持:

+
    +
  • 内容解锁专家:独家访问泰国本土流媒体平台、新闻网站和政府门户
  • +
  • 数据采集利器:从泰国服务器获取精准的本地化数据,助力市场调研
  • +
  • 隐私防护盾牌:为远程工作者和数字游民提供额外的安全层
  • +
  • 网络研究工具:帮助学者研究泰国网络生态的特殊性
  • +
+

我们的技术优势:不只是免费

+

我们提供的泰国代理服务之所以出众,源于三大技术创新:

+
    +
  1. 智能刷新系统:采用AI算法每180秒动态更新代理池,淘汰失效节点,确保95%以上的可用率
  2. +
  3. 协议矩阵支持: +
      +
    • HTTP(S):基础网页浏览的理想选择
    • +
    • SOCKS4:平衡速度与兼容性
    • +
    • SOCKS5:支持UDP和IPv6的进阶之选
    • +
    +
  4. +
  5. 三维筛选引擎:可同时按延迟(ms)、匿名等级(1-3级)、在线率(%)进行精准筛选
  6. +
+

性能优化建议

+

根据实测数据,我们建议:

+
    +
  • 视频流媒体:选择延迟<150ms的SOCKS5代理
  • +
  • 大数据采集:使用住宅代理轮换池
  • +
  • 即时通讯:固定1-2个高匿名节点
  • +
+

我们观察到一个有趣现象:莫斯科地区的代理在欧美时段(UTC+3 18:00-24:00)通常具有更好的国际带宽,这或许与当地ISP的流量调度策略有关。

+

您是否知道?泰国代理在应对某些国际CDN限制时表现出独特优势,这源于其特殊的网络基础设施布局。尝试不同的节点位置,可能会发现意外的连接优化效果。

+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/old/start.sh b/old/start.sh new file mode 100644 index 0000000..e6bf579 --- /dev/null +++ b/old/start.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +echo "========================================" +echo "WhereAmI - 多协议轮转代理工具" +echo "========================================" +echo "" + +# 检查Python环境 +echo "正在检查Python环境..." +if ! command -v python3 &> /dev/null; then + echo "[错误] 未找到Python3,请先安装Python 3.9+" + exit 1 +fi + +PYTHON_VERSION=$(python3 --version | awk '{print $2}' | cut -d. -f1,2) +echo "[成功] Python版本: $PYTHON_VERSION" +echo "" + +# 检查依赖包 +echo "正在检查依赖包..." +python3 -c "import PyQt6, requests, bs4, yaml, socks, loguru" 2>/dev/null +if [ $? -ne 0 ]; then + echo "[提示] 正在安装依赖包..." + pip3 install -r requirements.txt + if [ $? -ne 0 ]; then + echo "[错误] 依赖安装失败" + exit 1 + fi + echo "[成功] 依赖安装完成" +else + echo "[成功] 依赖包已安装" +fi + +echo "" +echo "========================================" +echo "启动 WhereAmI..." +echo "========================================" +echo "" + +python3 main.py + +if [ $? -ne 0 ]; then + echo "" + echo "[错误] 程序异常退出,请查看logs/whereami.log" + exit 1 +fi diff --git a/old/开发文档.md b/old/开发文档.md new file mode 100644 index 0000000..22db384 --- /dev/null +++ b/old/开发文档.md @@ -0,0 +1,248 @@ +# WhereAmI - 多协议轮转代理工具 + +## 项目概述 +**项目名称**:WhereAmI +**项目描述**:基于Python的轮转代理工具,将各种外部代理(HTTP/HTTPS/SOCKS4/SOCKS5)通过本地SOCKS5转发服务部署到127.0.0.1:8745,配合Proxifier使用 + +--- + +## 核心功能需求 + +### 1. 多协议支持 +- **输入协议**:HTTP、HTTPS、SOCKS4、SOCKS5 +- **输出协议**:统一转换为本地SOCKS5代理(127.0.0.1:8745) +- **实现方式**:python先连接对应代理,然后转发到本地8745端口,应用程序连接socks5代理的8745端口即可 + +### 2. 代理切换机制 +#### 2.1 手动切换模式 +- GUI界面提供代理列表,用户可手动选择并切换到指定代理 +- 显示当前使用代理的详细信息(IP、端口、延迟、状态等) + +#### 2.2 自动定时切换模式 +- **切换条件**: + 1. 达到设定的时间间隔(默认300秒,可配置) + 2. 且10秒内无活动连接(可配置) +- **切换策略**: + - 按顺序切换到下一个可用节点 + - 跳过已失效或延迟过高的节点 + - 记录切换历史 + +### 3. 代理获取方式 +#### 3.1 自动获取(网络抓取) +- **默认URL**:`http://cn.freevpnnode.com/free-proxy-for-thailand/` +- **可配置**:GUI中可修改抓取URL +- **解析流程**: + 1. 请求网页获取HTML内容 + 2. 提取表格中的代理信息(参考page.html结构) + 3. 字段映射:IP地址、端口、用户名、密码、国家、协议、匿名级别、速度、运行时间、延迟、更新时间 + 4. 生成JSON文件保存(格式参照local.json) + +#### 3.2 本地文件读取 +- **文件格式**:JSON(参照local.json模板) +- **必填字段**: + ```json + { + "ip_address": "string", + "port": "integer", + "username": "string (\"no need\"表示无需认证)", + "password": "string (\"no need\"表示无需认证)", + "protocol": "http|https|socks4|socks5" + } + ``` +- **可选字段**:country、anonymity、speed、uptime_percentage、response_time、latency、last_updated + +### 4. 代理健康检测 +#### 4.1 检测方法 +- **延迟测试**: + - TCP连接超时:5秒 + - 合格标准:< 500ms(可配置) + - 优秀标准:< 200ms + +- **连通性测试**: + - Google连接测试:访问 `https://www.google.com/` + - 超时时间:10秒 + - 预期响应:204状态码 + +#### 4.2 检测周期 +- **初始检测**:获取代理后立即全量检测 +- **定期复检**:每60秒检测一次正在使用的代理 +- **失败重试**:连续失败3次标记为不可用,10分钟后重新检测 + +#### 4.3 代理分级 +- **可用**:延迟<500ms 且 Google测试通过 +- **优秀**:延迟<200ms 且 Google测试通过 +- **不可用**:延迟>=500ms 或 Google测试失败 或 连续失败3次 + +### 5. GUI界面设计 +#### 5.1 主界面布局 +- **顶部区域**: + - 服务状态指示器(运行/停止) + - 当前代理信息卡片(IP、端口、协议、延迟、国家) + - 快速操作按钮(开始/停止、立即切换) + +- **中部区域**: + - 代理列表表格(支持排序、筛选) + - 列:IP、端口、协议、国家、延迟、状态、最后检测时间 + - 状态标识:可用 🟡一般 🔴不可用 + - 实时延迟趋势图(最近5分钟) + +- **底部区域**: + - 日志输出窗口(可清空) + - 统计信息(总代理数、可用数、已切换次数) + +#### 5.2 配置面板 +- **代理源设置**: + - 自动获取URL输入框 + - 本地文件路径选择 + - 刷新间隔设置(分钟) + +- **轮转策略设置**: + - 切换模式:手动 / 自动 + - 自动切换间隔(秒) + - 无活动等待时间(秒) + - 延迟阈值(毫秒) + +- **输出设置**: + - SOCKS5监听地址(默认127.0.0.1) + - SOCKS5监听端口(默认8745) + +- **检测设置**: + - 检测超时时间(秒) + - 连通性测试URL + - 复检间隔(秒) + +#### 5.3 交互功能 +- 右键菜单:复制代理信息、标记为优先、删除 +- 拖拽排序:调整代理优先级 +- 导入/导出:配置文件、代理列表 +- 历史记录:查看切换历史、性能统计 + +### 6. 配置管理 +#### 6.1 配置文件格式(config.yaml) +```yaml +# 代理源配置 +proxy_sources: + auto_fetch: + enabled: true + url: "http://cn.freevpnnode.com/free-proxy-for-thailand/" + refresh_interval: 10 # 分钟 + local_file: + enabled: true + path: "local.json" + +# 轮转策略 +rotation: + mode: "manual" # manual | auto + auto_switch_interval: 300 # 秒 + min_idle_time: 10 # 无活动连接最小等待时间(秒) + skip_high_latency: true # 跳过延迟>阈值的代理 + latency_threshold: 500 # 毫秒 + +# 输出配置 +output: + host: "127.0.0.1" + port: 8745 + +# 检测配置 +health_check: + timeout: 5 # TCP连接超时(秒) + connectivity_test_url: "https://www.google.com/" + connectivity_timeout: 10 # 连通性测试超时(秒) + recheck_interval: 60 # 复检间隔(秒) + max_failures: 3 # 最大失败次数 + retry_delay: 600 # 失败后重试延迟(秒) + +# 日志配置 +logging: + level: "INFO" # DEBUG | INFO | WARNING | ERROR + file: "whereami.log" + max_size: 10 # MB + backup_count: 5 +``` + +### 3. 数据流设计 +``` +[网页/本地文件] → [ProxyManager] → [HealthChecker] → [可用代理池] + ↓ +[RotationEngine] ← [无活动检测] ← [SOCKS Server] ← [客户端连接] + ↓ + [切换代理] → [更新SOCKS Server上游] +``` + +--- + +## 异常处理与容错 + +### 1. 常见异常场景 +- **所有代理失效**: + - 提示用户并尝试重新获取代理 + - 保持最后一次可用的代理继续服务 + - 后台持续检测新代理 + +- **网络波动**: + - 短暂断网(<30秒):保持当前代理,不切换 + - 长时间断网(>=30秒):暂停服务,显示警告 + - 网络恢复:自动重新检测并恢复服务 + +- **端口占用**: + - 检测8745端口是否被占用 + - 如被占用,提示用户关闭占用进程或更换端口 + +- **Proxifier未运行**: + - 仅作为提示,不影响程序运行 + - 在GUI中显示Proxifier状态建议 + +### 2. 数据安全 +- **认证信息加密**:使用Fernet对称加密存储username/password +- **日志脱敏**:日志中隐藏完整的IP和密码信息 +- **HTTPS证书**:测试时可选择跳过证书验证(配置项) + +--- + +## 性能优化 + +### 2. 缓存策略 +- **检测结果缓存**:缓存60秒,避免重复检测 +- **代理列表缓存**:本地JSON缓存,减少网络请求 +- **配置热重载**:修改配置后无需重启 + +### 3. 资源控制 +- **最大并发检测数**:10个(可配置) +- **代理池上限**:100个(可配置) +- **内存限制**:超过阈值时清理旧数据 + +--- + +## 扩展性设计 + +### 1. 插件化数据源 +- 定义DataSource接口,支持添加新的代理网站 +- 示例:ssrfree.com、proxynova.com等 + +### 2. 自定义测试脚本 +- 允许用户编写Python脚本自定义连通性测试方法 +- 通过配置文件指定测试脚本路径 + +### 3. API接口(未来) +- RESTful API:获取当前代理、切换代理、获取统计 +- WebSocket:实时推送代理状态变化 + + + + +GUI功能细化: +实时状态显示: + 当前使用的代理信息(IP、端口、协议、延迟) + 可用代理列表及状态(在线/离线/延迟) + 历史使用记录 +操作按钮: + 开始/停止服务 + 立即切换下一个代理 + 使用本地代理 + 获取免费代理 (使用cn.freevpnnode.com源) + 获取其他代理 (预留按钮,启动scratch.py并检测) + 导入/导出配置 +监控图表: + 延迟趋势图 + 切换历史记录 +使用一个次级页面展示配置并且可以在GUI上快速更改 \ No newline at end of file diff --git a/proxy.json b/proxy.json new file mode 100644 index 0000000..11f0352 --- /dev/null +++ b/proxy.json @@ -0,0 +1,1442 @@ +[ + { + "ip_address": "171.6.75.111", + "port": 8080, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "98%", + "response_time": "4797 ms", + "latency": "timeout", + "last_updated": "3 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "202.139.198.15", + "port": 3030, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "100%", + "response_time": "1777 ms", + "latency": "timeout", + "last_updated": "4 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "116.58.254.201", + "port": 8080, + "username": "no need", + "password": "no need", + "protocol": "socks5", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "79%", + "response_time": "4311 ms", + "latency": "timeout", + "last_updated": "8 d", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "118.173.233.39", + "port": 4898, + "username": "no need", + "password": "no need", + "protocol": "socks5", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "46%", + "response_time": "5202 ms", + "latency": "timeout", + "last_updated": "3 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "118.174.152.156", + "port": 8080, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "68%", + "response_time": "4884 ms", + "latency": "timeout", + "last_updated": "4 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "203.158.221.152", + "port": 80, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "100%", + "response_time": "3379 ms", + "latency": "timeout", + "last_updated": "3 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "110.78.138.125", + "port": 8080, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "83%", + "response_time": "3898 ms", + "latency": "timeout", + "last_updated": "3 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "61.7.138.214", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "87%", + "response_time": "3699 ms", + "latency": "timeout", + "last_updated": "8 d", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "61.7.184.130", + "port": 4153, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "80%", + "response_time": "4076 ms", + "latency": "timeout", + "last_updated": "1 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "101.51.88.247", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "65%", + "response_time": "3112 ms", + "latency": "timeout", + "last_updated": "1 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "118.174.219.165", + "port": 2168, + "username": "no need", + "password": "no need", + "protocol": "socks5", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "81%", + "response_time": "2113 ms", + "latency": "timeout", + "last_updated": "6 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "110.78.149.221", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "76%", + "response_time": "4301 ms", + "latency": "timeout", + "last_updated": "4 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "101.109.100.69", + "port": 8080, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "46%", + "response_time": "1624 ms", + "latency": "timeout", + "last_updated": "2 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "110.78.82.70", + "port": 5678, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "93%", + "response_time": "3402 ms", + "latency": "timeout", + "last_updated": "7 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "134.236.16.125", + "port": 8080, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "68%", + "response_time": "3804 ms", + "latency": "timeout", + "last_updated": "2 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "1.2.212.35", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "69%", + "response_time": "4428 ms", + "latency": "timeout", + "last_updated": "3 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "110.78.149.110", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "74%", + "response_time": "4886 ms", + "latency": "timeout", + "last_updated": "8 d", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "110.77.228.176", + "port": 80, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "13%", + "response_time": "2998 ms", + "latency": "timeout", + "last_updated": "3 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "101.109.107.144", + "port": 8080, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "48%", + "response_time": "4099 ms", + "latency": "timeout", + "last_updated": "8 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "118.174.162.12", + "port": 8080, + "username": "no need", + "password": "no need", + "protocol": "socks5", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "63%", + "response_time": "2384 ms", + "latency": "timeout", + "last_updated": "7 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "61.7.184.216", + "port": 4153, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "79%", + "response_time": "4906 ms", + "latency": "timeout", + "last_updated": "8 d", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "101.51.241.126", + "port": 4153, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "52%", + "response_time": "4505 ms", + "latency": "timeout", + "last_updated": "8 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "119.59.101.111", + "port": 80, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "100%", + "response_time": "2280 ms", + "latency": "timeout", + "last_updated": "8 d", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "1.179.148.33", + "port": 1080, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "94%", + "response_time": "4387 ms", + "latency": "timeout", + "last_updated": "2 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "223.205.189.164", + "port": 5678, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "97%", + "response_time": "4104 ms", + "latency": "timeout", + "last_updated": "8 d", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "110.78.186.220", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "62%", + "response_time": "2796 ms", + "latency": "timeout", + "last_updated": "8 d", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "101.109.48.54", + "port": 8080, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "75%", + "response_time": "3782 ms", + "latency": "timeout", + "last_updated": "2 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "171.6.78.164", + "port": 8080, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "100%", + "response_time": "5006 ms", + "latency": "timeout", + "last_updated": "2 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "134.236.30.92", + "port": 5678, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "59%", + "response_time": "3696 ms", + "latency": "timeout", + "last_updated": "8 d", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "118.173.230.19", + "port": 1081, + "username": "no need", + "password": "no need", + "protocol": "socks5", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "99%", + "response_time": "3001 ms", + "latency": "timeout", + "last_updated": "1 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "182.52.70.117", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "99%", + "response_time": "4966 ms", + "latency": "timeout", + "last_updated": "2 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "110.78.149.65", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "78%", + "response_time": "3127 ms", + "latency": "timeout", + "last_updated": "7 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "110.78.149.12", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "72%", + "response_time": "3608 ms", + "latency": "timeout", + "last_updated": "8 d", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "1.1.193.51", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "40%", + "response_time": "2268 ms", + "latency": "timeout", + "last_updated": "2 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "118.173.230.149", + "port": 1080, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "69%", + "response_time": "3984 ms", + "latency": "timeout", + "last_updated": "3 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "110.78.149.34", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "73%", + "response_time": "4899 ms", + "latency": "timeout", + "last_updated": "1 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "110.78.149.43", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "77%", + "response_time": "3408 ms", + "latency": "timeout", + "last_updated": "8 d", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "125.26.224.230", + "port": 8080, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "76%", + "response_time": "1992 ms", + "latency": "timeout", + "last_updated": "6 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "1.4.157.35", + "port": 4694, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "99%", + "response_time": "2973 ms", + "latency": "timeout", + "last_updated": "56 sec", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "110.77.232.172", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "80%", + "response_time": "5092 ms", + "latency": "timeout", + "last_updated": "8 d", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "115.178.63.22", + "port": 12736, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "93%", + "response_time": "4418 ms", + "latency": "timeout", + "last_updated": "2 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "49.48.93.179", + "port": 8080, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "97%", + "response_time": "3907 ms", + "latency": "timeout", + "last_updated": "8 d", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "61.7.138.226", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "76%", + "response_time": "5072 ms", + "latency": "timeout", + "last_updated": "8 d", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "118.174.219.5", + "port": 4153, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "85%", + "response_time": "1228 ms", + "latency": "timeout", + "last_updated": "6 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "110.77.184.106", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "87%", + "response_time": "4271 ms", + "latency": "timeout", + "last_updated": "2 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "134.236.117.98", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "42%", + "response_time": "3519 ms", + "latency": "timeout", + "last_updated": "8 d", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "1.0.136.76", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "56%", + "response_time": "4982 ms", + "latency": "timeout", + "last_updated": "8 d", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "1.10.133.19", + "port": 8080, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "89%", + "response_time": "3570 ms", + "latency": "timeout", + "last_updated": "3 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "159.138.255.141", + "port": 8080, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "82%", + "response_time": "703 ms", + "latency": "timeout", + "last_updated": "1 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "1.0.163.213", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "59%", + "response_time": "3782 ms", + "latency": "timeout", + "last_updated": "1 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "1.10.232.206", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "65%", + "response_time": "2694 ms", + "latency": "timeout", + "last_updated": "8 d", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "110.78.149.139", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "71%", + "response_time": "4405 ms", + "latency": "timeout", + "last_updated": "4 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "110.78.149.159", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "70%", + "response_time": "46 ms", + "latency": "timeout", + "last_updated": "24 sec", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "1.20.169.77", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "84%", + "response_time": "3794 ms", + "latency": "timeout", + "last_updated": "8 d", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "223.204.69.116", + "port": 4153, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "83%", + "response_time": "3718 ms", + "latency": "timeout", + "last_updated": "8 d", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "1.10.133.77", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "70%", + "response_time": "3991 ms", + "latency": "timeout", + "last_updated": "4 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "125.27.10.84", + "port": 4153, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "98%", + "response_time": "5012 ms", + "latency": "timeout", + "last_updated": "8 d", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "110.78.148.227", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "84%", + "response_time": "491 ms", + "latency": "timeout", + "last_updated": "8 d", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "125.26.22.7", + "port": 5678, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "94%", + "response_time": "4176 ms", + "latency": "timeout", + "last_updated": "3 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "101.108.12.69", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "60%", + "response_time": "4909 ms", + "latency": "timeout", + "last_updated": "8 d", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "1.179.148.9", + "port": 36476, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "97%", + "response_time": "4386 ms", + "latency": "timeout", + "last_updated": "7 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "101.108.0.189", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "55%", + "response_time": "3893 ms", + "latency": "timeout", + "last_updated": "8 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "1.20.169.71", + "port": 4153, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "83%", + "response_time": "3984 ms", + "latency": "timeout", + "last_updated": "8 d", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "110.164.163.34", + "port": 52517, + "username": "no need", + "password": "no need", + "protocol": "socks5", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "100%", + "response_time": "158 ms", + "latency": "timeout", + "last_updated": "6 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "101.109.245.154", + "port": 55644, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "99%", + "response_time": "4698 ms", + "latency": "timeout", + "last_updated": "1 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "110.238.116.82", + "port": 8015, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "100%", + "response_time": "2693 ms", + "latency": "timeout", + "last_updated": "6 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "223.27.210.77", + "port": 8080, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "100%", + "response_time": "902 ms", + "latency": "timeout", + "last_updated": "6 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "184.82.76.136", + "port": 8080, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "55%", + "response_time": "4275 ms", + "latency": "timeout", + "last_updated": "1 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "134.236.58.204", + "port": 8080, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "61%", + "response_time": "3701 ms", + "latency": "timeout", + "last_updated": "54 sec", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "1.10.229.230", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "50%", + "response_time": "3919 ms", + "latency": "timeout", + "last_updated": "8 d", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "118.174.209.189", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "68%", + "response_time": "5119 ms", + "latency": "timeout", + "last_updated": "4 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "118.172.47.97", + "port": 51327, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "98%", + "response_time": "4303 ms", + "latency": "timeout", + "last_updated": "7 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "1.10.133.134", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "70%", + "response_time": "5096 ms", + "latency": "timeout", + "last_updated": "4 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "110.78.147.184", + "port": 8080, + "username": "no need", + "password": "no need", + "protocol": "socks5", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "82%", + "response_time": "2331 ms", + "latency": "timeout", + "last_updated": "2 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "1.0.136.207", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "53%", + "response_time": "4876 ms", + "latency": "timeout", + "last_updated": "8 d", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "171.6.119.82", + "port": 8080, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "95%", + "response_time": "3282 ms", + "latency": "timeout", + "last_updated": "16 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "203.170.222.4", + "port": 8080, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "97%", + "response_time": "4703 ms", + "latency": "timeout", + "last_updated": "2 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "1.10.238.200", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "44%", + "response_time": "3482 ms", + "latency": "timeout", + "last_updated": "7 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "1.10.133.237", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "61%", + "response_time": "2302 ms", + "latency": "timeout", + "last_updated": "2 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "101.109.251.42", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "99%", + "response_time": "4022 ms", + "latency": "timeout", + "last_updated": "8 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "110.77.236.112", + "port": 4153, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "87%", + "response_time": "4819 ms", + "latency": "timeout", + "last_updated": "1 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "61.7.138.69", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "85%", + "response_time": "4196 ms", + "latency": "timeout", + "last_updated": "8 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "101.108.178.143", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks5", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "59%", + "response_time": "1186 ms", + "latency": "timeout", + "last_updated": "5 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "49.48.120.123", + "port": 8080, + "username": "no need", + "password": "no need", + "protocol": "socks5", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "98%", + "response_time": "4878 ms", + "latency": "timeout", + "last_updated": "8 d", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "203.150.113.249", + "port": 14153, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "8%", + "response_time": "4267 ms", + "latency": "timeout", + "last_updated": "3 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "113.53.91.10", + "port": 5678, + "username": "no need", + "password": "no need", + "protocol": "socks5", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "98%", + "response_time": "213 ms", + "latency": "timeout", + "last_updated": "3 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "119.42.71.103", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "74%", + "response_time": "2812 ms", + "latency": "timeout", + "last_updated": "8 d", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "1.4.145.244", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "67%", + "response_time": "589 ms", + "latency": "timeout", + "last_updated": "8 d", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "1.2.214.8", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "70%", + "response_time": "5066 ms", + "latency": "timeout", + "last_updated": "7 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + }, + { + "ip_address": "101.51.109.214", + "port": 4145, + "username": "no need", + "password": "no need", + "protocol": "socks4", + "country": "泰国", + "anonymity": "elite (HIA)", + "speed": "1 ms", + "uptime_percentage": "49%", + "response_time": "4599 ms", + "latency": "timeout", + "last_updated": "6 min", + "status": "unavailable", + "last_checked": "2026-06-15T00:11:01.709800" + } +] \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..c7904d6 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,31 @@ +# WhereAmI - 多协议轮转代理工具依赖包 + +# GUI框架 +PyQt6>=6.4.0 + +# 网络请求 +requests>=2.31.0 +aiohttp>=3.8.0 +aiohttp-socks>=0.8.0 + +# HTML解析 +beautifulsoup4>=4.12.0 +lxml>=4.9.0 + +# 配置文件 +PyYAML>=6.0.1 + +# SOCKS支持 +PySocks>=1.7.1 + +# 加密 +cryptography>=41.0.0 + +# 系统监控 +psutil>=5.9.0 + +# 日志 +loguru>=0.7.0 + +# 图表(可选) +matplotlib>=3.7.0 diff --git a/scratch.py b/scratch.py new file mode 100644 index 0000000..add52fb --- /dev/null +++ b/scratch.py @@ -0,0 +1,172 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Scratch 测试脚本 - 预留用于代理测试 + +此文件用于快速测试和实验新功能。 +可以通过 GUI 的"获取其他代理(预留)"按钮启动此脚本。 + +使用方法: +1. 在此文件中编写测试代码 +2. 运行: python scratch.py +3. 观察测试结果 +4. 根据需要调整代码 + +注意: +- 此文件不会被版本控制追踪 +- 可以随时修改和重写 +- 适合快速原型开发和调试 +""" + +import sys +import os +from loguru import logger + +# 添加项目根目录到路径 +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +from core import ProxyManager, DataSource, HealthChecker +from core.models import ProxyInfo, ProxyProtocol, ProxyStatus + + +def test_proxy_connection(): + """测试代理连接""" + logger.info("=" * 60) + logger.info("开始代理连接测试") + logger.info("=" * 60) + + # 示例:测试单个代理 + proxy = ProxyInfo( + ip_address="127.0.0.1", + port=1080, + username="no need", + password="no need", + protocol=ProxyProtocol.SOCKS5, + country="CN" + ) + + logger.info(f"测试代理: {proxy.get_address()}") + + # TODO: 在这里添加您的测试代码 + # 例如: + # - 测试代理连通性 + # - 测试代理速度 + # - 测试代理匿名性 + # - 批量测试多个代理 + + logger.info("测试完成") + + +def test_data_source(): + """测试数据源""" + logger.info("=" * 60) + logger.info("开始数据源测试") + logger.info("=" * 60) + + config = { + 'proxy_sources': { + 'auto_fetch': { + 'enabled': False, + 'url': '', + 'output_file': 'proxy.json' + }, + 'local_file': { + 'enabled': True, + 'path': 'local.json' + } + } + } + + data_source = DataSource(config) + + # 加载本地代理 + proxies = data_source.load_from_file('local.json') + logger.info(f"加载了 {len(proxies)} 个代理") + + for i, proxy in enumerate(proxies, 1): + logger.info(f"代理{i}: {proxy.get_address()} ({proxy.protocol.value})") + + logger.info("数据源测试完成") + + +def test_health_check(): + """测试健康检测""" + logger.info("=" * 60) + logger.info("开始健康检测测试") + logger.info("=" * 60) + + config = { + 'health_check': { + 'timeout': 5, + 'connectivity_test_url': 'https://www.google.com/', + 'connectivity_timeout': 10 + } + } + + checker = HealthChecker(config) + + # 创建测试代理 + proxy = ProxyInfo( + ip_address="127.0.0.1", + port=1080, + protocol=ProxyProtocol.SOCKS5 + ) + + logger.info(f"检测代理: {proxy.get_address()}") + status = checker.check_proxy(proxy) + + logger.info(f"检测结果: {status}") + logger.info(f"延迟: {proxy.latency_ms:.0f}ms") + + logger.info("健康检测测试完成") + + +def main(): + """主函数""" + logger.info("Scratch 测试脚本启动") + logger.info("=" * 60) + + # 选择要运行的测试 + tests = { + '1': ('代理连接测试', test_proxy_connection), + '2': ('数据源测试', test_data_source), + '3': ('健康检测测试', test_health_check), + } + + logger.info("可用测试:") + for key, (name, _) in tests.items(): + logger.info(f" {key}. {name}") + + choice = input("\n请选择测试 (1/2/3),或直接回车运行所有测试: ").strip() + + if choice in tests: + # 运行选定的测试 + test_name, test_func = tests[choice] + logger.info(f"\n运行测试: {test_name}") + try: + test_func() + except Exception as e: + logger.error(f"测试失败: {e}") + import traceback + traceback.print_exc() + else: + # 运行所有测试 + logger.info("\n运行所有测试") + for test_name, test_func in tests.values(): + logger.info(f"\n{'='*60}") + logger.info(f"运行: {test_name}") + logger.info(f"{'='*60}") + try: + test_func() + except Exception as e: + logger.error(f"测试失败: {e}") + import traceback + traceback.print_exc() + + logger.info("\n" + "=" * 60) + logger.info("所有测试完成") + logger.info("=" * 60) + + +if __name__ == "__main__": + main() diff --git a/start.bat b/start.bat new file mode 100644 index 0000000..e35324d --- /dev/null +++ b/start.bat @@ -0,0 +1,44 @@ +@echo off +chcp 65001 >nul +echo ======================================== +echo WhereAmI - 多协议轮转代理工具 +echo ======================================== +echo. +echo 正在检查Python环境... +python --version >nul 2>&1 +if errorlevel 1 ( + echo [错误] 未找到Python,请先安装Python 3.9+ + pause + exit /b 1 +) + +echo [成功] Python环境正常 +echo. +echo 正在检查依赖包... +python -c "import PyQt6, requests, bs4, yaml, socks, loguru" >nul 2>&1 +if errorlevel 1 ( + echo [提示] 正在安装依赖包... + pip install -r requirements.txt + if errorlevel 1 ( + echo [错误] 依赖安装失败 + pause + exit /b 1 + ) + echo [成功] 依赖安装完成 +) else ( + echo [成功] 依赖包已安装 +) + +echo. +echo ======================================== +echo 启动 WhereAmI... +echo ======================================== +echo. + +python main.py + +if errorlevel 1 ( + echo. + echo [错误] 程序异常退出,请查看logs/whereami.log + pause +)