This commit is contained in:
MasonLiu 2026-06-15 00:49:26 +08:00
commit 366a84fa89
24 changed files with 5303 additions and 0 deletions

267
README.md Normal file
View File

@ -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 <repository-url>
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

17
config.yaml Normal file
View File

@ -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

16
core/__init__.py Normal file
View File

@ -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'
]

219
core/data_source.py Normal file
View File

@ -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)}")

192
core/health_checker.py Normal file
View File

@ -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)

98
core/models.py Normal file
View File

@ -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

266
core/proxy_manager.py Normal file
View File

@ -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)

View File

@ -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

124
diagnose.py Normal file
View File

@ -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()

4
gui/__init__.py Normal file
View File

@ -0,0 +1,4 @@
"""GUI模块"""
from .main_window import MainWindow
__all__ = ['MainWindow']

Binary file not shown.

Binary file not shown.

587
gui/main_window.py Normal file
View File

@ -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()

34
local.json Normal file
View File

@ -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"
}
]

307
logs/whereami.log Normal file
View File

@ -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 - 代理服务已停止

65
main.py Normal file
View File

@ -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='<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level: <8}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>'
)
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()

BIN
old/old_design.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 417 KiB

739
old/page.html Normal file
View File

@ -0,0 +1,739 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<link rel="canonical" href="https://cn.freevpnnode.com/free-proxy-for-thailand/" />
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>泰国免费代理服务器列表 | 实时更新高匿名代理 - Free VPN Node, 第1页</title>
<link rel="icon" href="https://cn.freevpnnode.com/favicon.ico" type="image/x-icon"/>
<meta name="keywords" content="泰国免费代理, 高匿名代理, SOCKS4代理, SOCKS5代理, HTTP代理, 免费代理列表, 泰国IP, 代理服务器下载, 实时代理更新" />
<meta name="description" content="Free VPN Node 提供全面的泰国免费代理服务器列表,支持 SOCKS4、SOCKS5 和 HTTP 协议。我们的代理列表每三分钟更新一次,确保数据的实时性和可靠性。用户可按匿名级别、协议类型、速度等参数筛选代理,并以 JSON、TXT、CSV 格式下载,方便集成至各类应用程序。无论是用于匿名浏览、绕过地理限制,还是进行数据抓取,我们的高匿名代理都能满足您的需求,助您安全、快速地访问互联网。" />
<meta property="og:type" content="article" />
<meta property="og:url" content="https://cn.freevpnnode.com/free-proxy-for-thailand/" />
<meta property="og:site_name" content="免费VPN节点" />
<meta property="og:title" content="泰国免费代理服务器列表 | 实时更新高匿名代理 - Free VPN Node, 第1页" />
<meta property="og:image" content="https://cn.freevpnnode.com.webp" />
<meta property="og:description" content="Free VPN Node 提供全面的泰国免费代理服务器列表,支持 SOCKS4、SOCKS5 和 HTTP 协议。我们的代理列表每三分钟更新一次,确保数据的实时性和可靠性。用户可按匿名级别、协议类型、速度等参数筛选代理,并以 JSON、TXT、CSV 格式下载,方便集成至各类应用程序。无论是用于匿名浏览、绕过地理限制,还是进行数据抓取,我们的高匿名代理都能满足您的需求,助您安全、快速地访问互联网。" />
<meta name="applicable-device" content="pc,mobile" />
<meta http-equiv="Cache-Control" content="no-transform" />
<meta name="robots" content="max-image-preview:large" />
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="免费VPN节点">
<meta name="format-detection" content="telephone=no">
<link href="/assets/libs/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="/assets/css/style.css" rel="stylesheet">
<link href="/assets/css/base.css" rel="stylesheet">
<!-- HTML5 shim, for IE6-8 support of HTML5 elements. All other JS at the end of file. -->
<!--[if lt IE 9]>
<script src="/assets/js/html5shiv.js"></script>
<script src="/assets/js/respond.min.js"></script>
<![endif]-->
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-LGZ2YLLXB4"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-LGZ2YLLXB4');
</script>
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3332997411212854" crossorigin="anonymous"></script>
</head>
<body>
<header class="sticky-top">
<div class="container">
<nav class="navbar navbar-default m-0">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand py-0" href="/">
<img src="/assets/img/logo.png" alt="logo" title="">
</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li class="text-center ">
<a href="/" title="首页">
首页 </a>
</li>
<li class="text-center ">
<a href="/country/" title="国家">
国家 </a>
</li>
<li class="text-center ">
<a href="/free-proxy/" title="免费代理">
免费代理 </a>
</li>
<li class="text-center ">
<a href="/free-vpn/" title="免费VPN">
免费VPN </a>
</li>
</ul>
</div>
</div>
</nav>
</div>
</header>
<main class="pt-5">
<div class="container">
<div class="row mt-3">
<ol class="breadcrumb">
<li><a href="/">首页</a></li>
<li><a href="/free-proxy/">免费代理服务器汇总</a></li>
<li class="active">免费泰国代理服务器汇总</li>
</ol>
</div>
<div class="row">
<h1>免费泰国代理服务器汇总 - 高速、稳定、实时更新</h1>
<p>想要访问泰国本地内容、绕过地域限制或增强网络匿名性?我们提供全球最大的免费泰国代理服务器列表之一,涵盖 HTTP(S)、SOCKS4 和 SOCKS5 协议,满足不同需求。</p>
<h2>为什么选择我们的泰国代理?</h2>
<ol>
<li><strong>实时更新:</strong>我们的系统每 3 分钟检测一次,剔除失效代理,确保你获取的 IP 始终可用。</li>
<li><strong>严格筛选:</strong>每天自动验证超过 100 万个代理,只保留高速、稳定的服务器。</li>
<li><strong>广泛兼容:</strong>支持浏览器、爬虫脚本、数据分析工具等,轻松集成到你的工作流程中。</li>
</ol>
<h3>如何高效使用?</h3>
<ul>
<li><strong>按协议筛选:</strong>SOCKS5 适合高隐私需求HTTP(S) 适合普通网页访问。</li>
<li><strong>检查延迟:</strong>免费代理速度差异大,建议测试多个 IP 选择最优连接。</li>
<li><strong>注意安全性:</strong>避免在免费代理上登录银行账户或传输敏感数据,以防信息泄露。</li>
</ul>
<h3>免费 vs. 付费代理</h3>
<p>虽然我们的列表完全免费,但付费代理通常更稳定、速度更快。如果你需要长期稳定的泰国 IP可以考虑低延迟的付费方案。</p>
<p>现在,试试这些最新可用的泰国代理,解锁地理限制内容吧! </p>
</div>
<div class="row">
<div style="background:#fcf8e3;border:1px solid #faf3cd;border-left: 0.5rem solid #faf3cd;padding: 0.5rem;line-height:2rem;margin-top: 1rem;color:#c09853;">2026.06.13分享SSR、V2Ray、Clash免费节点包含美国、韩国、德国、日本、新加坡免费节点仅供学习研究请勿非法使用。 <a href="#" target="_blank" id="js-ad_href">【查看详情】</a></div>
</div>
<div class="row vpn-list">
<table class="table table-hover table-striped">
<thead>
<tr>
<th>IP地址</th>
<th>端口号</th>
<th>用户名</th>
<th>密码</th>
<th>国家</th>
<th>协议</th>
<th>Anonymity</th>
<th>速度</th>
<th>运行时间</th>
<th>回复</th>
<th>延迟</th>
<th>更新</th>
</tr>
</thead>
<tbody>
<tr>
<td>171.6.75.111</td>
<td>8080</td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td>
<a href="/free-proxy-for-thailand/" title="泰国">
<img width="20" height="20" alt="泰国" src="/uploads/flag/thailand.svg">
TH </a>
</td>
<td>socks4</td>
<td>elite (HIA)</td>
<td>1 ms</td>
<td>98%</td>
<td>4797 ms</td>
<td>160 ms</td>
<td>3 min</td>
</tr>
<tr>
<td>202.139.198.15</td>
<td>3030</td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td>
<a href="/free-proxy-for-thailand/" title="泰国">
<img width="20" height="20" alt="泰国" src="/uploads/flag/thailand.svg">
TH </a>
</td>
<td>socks4</td>
<td>elite (HIA)</td>
<td>1 ms</td>
<td>100%</td>
<td>1777 ms</td>
<td>168 ms</td>
<td>4 min</td>
</tr>
<tr>
<td>116.58.254.201</td>
<td>8080</td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td>
<a href="/free-proxy-for-thailand/" title="泰国">
<img width="20" height="20" alt="泰国" src="/uploads/flag/thailand.svg">
TH </a>
</td>
<td>socks5</td>
<td>elite (HIA)</td>
<td>1 ms</td>
<td>79%</td>
<td>4311 ms</td>
<td>168 ms</td>
<td>8 d</td>
</tr>
<tr>
<td>118.173.233.39</td>
<td>4898</td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td>
<a href="/free-proxy-for-thailand/" title="泰国">
<img width="20" height="20" alt="泰国" src="/uploads/flag/thailand.svg">
TH </a>
</td>
<td>socks5</td>
<td>elite (HIA)</td>
<td>1 ms</td>
<td>46%</td>
<td>5202 ms</td>
<td>169 ms</td>
<td>3 min</td>
</tr>
<tr>
<td>118.174.152.156</td>
<td>8080</td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td>
<a href="/free-proxy-for-thailand/" title="泰国">
<img width="20" height="20" alt="泰国" src="/uploads/flag/thailand.svg">
TH </a>
</td>
<td>socks4</td>
<td>elite (HIA)</td>
<td>1 ms</td>
<td>68%</td>
<td>4884 ms</td>
<td>169 ms</td>
<td>4 min</td>
</tr>
<tr>
<td>203.158.221.152</td>
<td>80</td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td>
<a href="/free-proxy-for-thailand/" title="泰国">
<img width="20" height="20" alt="泰国" src="/uploads/flag/thailand.svg">
TH </a>
</td>
<td>socks4</td>
<td>elite (HIA)</td>
<td>1 ms</td>
<td>100%</td>
<td>3379 ms</td>
<td>170 ms</td>
<td>3 min</td>
</tr>
<tr>
<td>110.78.138.125</td>
<td>8080</td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td>
<a href="/free-proxy-for-thailand/" title="泰国">
<img width="20" height="20" alt="泰国" src="/uploads/flag/thailand.svg">
TH </a>
</td>
<td>socks4</td>
<td>elite (HIA)</td>
<td>1 ms</td>
<td>83%</td>
<td>3898 ms</td>
<td>170 ms</td>
<td>3 min</td>
</tr>
<tr>
<td>61.7.138.214</td>
<td>4145</td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td>
<a href="/free-proxy-for-thailand/" title="泰国">
<img width="20" height="20" alt="泰国" src="/uploads/flag/thailand.svg">
TH </a>
</td>
<td>socks4</td>
<td>elite (HIA)</td>
<td>1 ms</td>
<td>87%</td>
<td>3699 ms</td>
<td>170 ms</td>
<td>8 d</td>
</tr>
<tr>
<td>61.7.184.130</td>
<td>4153</td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td>
<a href="/free-proxy-for-thailand/" title="泰国">
<img width="20" height="20" alt="泰国" src="/uploads/flag/thailand.svg">
TH </a>
</td>
<td>socks4</td>
<td>elite (HIA)</td>
<td>1 ms</td>
<td>80%</td>
<td>4076 ms</td>
<td>170 ms</td>
<td>1 min</td>
</tr>
<tr>
<td>101.51.88.247</td>
<td>4145</td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td>
<a href="/free-proxy-for-thailand/" title="泰国">
<img width="20" height="20" alt="泰国" src="/uploads/flag/thailand.svg">
TH </a>
</td>
<td>socks4</td>
<td>elite (HIA)</td>
<td>1 ms</td>
<td>65%</td>
<td>3112 ms</td>
<td>170 ms</td>
<td>1 min</td>
</tr>
<tr>
<td>118.174.219.165</td>
<td>2168</td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td>
<a href="/free-proxy-for-thailand/" title="泰国">
<img width="20" height="20" alt="泰国" src="/uploads/flag/thailand.svg">
TH </a>
</td>
<td>socks5</td>
<td>elite (HIA)</td>
<td>1 ms</td>
<td>81%</td>
<td>2113 ms</td>
<td>171 ms</td>
<td>6 min</td>
</tr>
<tr>
<td>110.78.149.221</td>
<td>4145</td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td>
<a href="/free-proxy-for-thailand/" title="泰国">
<img width="20" height="20" alt="泰国" src="/uploads/flag/thailand.svg">
TH </a>
</td>
<td>socks4</td>
<td>elite (HIA)</td>
<td>1 ms</td>
<td>76%</td>
<td>4301 ms</td>
<td>172 ms</td>
<td>4 min</td>
</tr>
<tr>
<td>101.109.100.69</td>
<td>8080</td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td>
<a href="/free-proxy-for-thailand/" title="泰国">
<img width="20" height="20" alt="泰国" src="/uploads/flag/thailand.svg">
TH </a>
</td>
<td>socks4</td>
<td>elite (HIA)</td>
<td>1 ms</td>
<td>46%</td>
<td>1624 ms</td>
<td>172 ms</td>
<td>2 min</td>
</tr>
<tr>
<td>110.78.82.70</td>
<td>5678</td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td>
<a href="/free-proxy-for-thailand/" title="泰国">
<img width="20" height="20" alt="泰国" src="/uploads/flag/thailand.svg">
TH </a>
</td>
<td>socks4</td>
<td>elite (HIA)</td>
<td>1 ms</td>
<td>93%</td>
<td>3402 ms</td>
<td>172 ms</td>
<td>7 min</td>
</tr>
<tr>
<td>134.236.16.125</td>
<td>8080</td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td>
<a href="/free-proxy-for-thailand/" title="泰国">
<img width="20" height="20" alt="泰国" src="/uploads/flag/thailand.svg">
TH </a>
</td>
<td>socks4</td>
<td>elite (HIA)</td>
<td>1 ms</td>
<td>68%</td>
<td>3804 ms</td>
<td>172 ms</td>
<td>2 min</td>
</tr>
<tr>
<td>1.2.212.35</td>
<td>4145</td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td>
<a href="/free-proxy-for-thailand/" title="泰国">
<img width="20" height="20" alt="泰国" src="/uploads/flag/thailand.svg">
TH </a>
</td>
<td>socks4</td>
<td>elite (HIA)</td>
<td>1 ms</td>
<td>69%</td>
<td>4428 ms</td>
<td>172 ms</td>
<td>3 min</td>
</tr>
<tr>
<td>110.78.149.110</td>
<td>4145</td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td>
<a href="/free-proxy-for-thailand/" title="泰国">
<img width="20" height="20" alt="泰国" src="/uploads/flag/thailand.svg">
TH </a>
</td>
<td>socks4</td>
<td>elite (HIA)</td>
<td>1 ms</td>
<td>74%</td>
<td>4886 ms</td>
<td>172 ms</td>
<td>8 d</td>
</tr>
<tr>
<td>110.77.228.176</td>
<td>80</td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td>
<a href="/free-proxy-for-thailand/" title="泰国">
<img width="20" height="20" alt="泰国" src="/uploads/flag/thailand.svg">
TH </a>
</td>
<td>socks4</td>
<td>elite (HIA)</td>
<td>1 ms</td>
<td>13%</td>
<td>2998 ms</td>
<td>172 ms</td>
<td>3 min</td>
</tr>
<tr>
<td>101.109.107.144</td>
<td>8080</td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td>
<a href="/free-proxy-for-thailand/" title="泰国">
<img width="20" height="20" alt="泰国" src="/uploads/flag/thailand.svg">
TH </a>
</td>
<td>socks4</td>
<td>elite (HIA)</td>
<td>1 ms</td>
<td>48%</td>
<td>4099 ms</td>
<td>172 ms</td>
<td>8 min</td>
</tr>
<tr>
<td>118.174.162.12</td>
<td>8080</td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td>
<a href="/free-proxy-for-thailand/" title="泰国">
<img width="20" height="20" alt="泰国" src="/uploads/flag/thailand.svg">
TH </a>
</td>
<td>socks5</td>
<td>elite (HIA)</td>
<td>1 ms</td>
<td>63%</td>
<td>2384 ms</td>
<td>172 ms</td>
<td>7 min</td>
</tr>
<tr>
<td>61.7.184.216</td>
<td>4153</td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td>
<a href="/free-proxy-for-thailand/" title="泰国">
<img width="20" height="20" alt="泰国" src="/uploads/flag/thailand.svg">
TH </a>
</td>
<td>socks4</td>
<td>elite (HIA)</td>
<td>1 ms</td>
<td>79%</td>
<td>4906 ms</td>
<td>173 ms</td>
<td>8 d</td>
</tr>
<tr>
<td>101.51.241.126</td>
<td>4153</td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td>
<a href="/free-proxy-for-thailand/" title="泰国">
<img width="20" height="20" alt="泰国" src="/uploads/flag/thailand.svg">
TH </a>
</td>
<td>socks4</td>
<td>elite (HIA)</td>
<td>1 ms</td>
<td>52%</td>
<td>4505 ms</td>
<td>173 ms</td>
<td>8 min</td>
</tr>
<tr>
<td>119.59.101.111</td>
<td>80</td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td>
<a href="/free-proxy-for-thailand/" title="泰国">
<img width="20" height="20" alt="泰国" src="/uploads/flag/thailand.svg">
TH </a>
</td>
<td>socks4</td>
<td>elite (HIA)</td>
<td>1 ms</td>
<td>100%</td>
<td>2280 ms</td>
<td>173 ms</td>
<td>8 d</td>
</tr>
<tr>
<td>1.179.148.33</td>
<td>1080</td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td>
<a href="/free-proxy-for-thailand/" title="泰国">
<img width="20" height="20" alt="泰国" src="/uploads/flag/thailand.svg">
TH </a>
</td>
<td>socks4</td>
<td>elite (HIA)</td>
<td>1 ms</td>
<td>94%</td>
<td>4387 ms</td>
<td>173 ms</td>
<td>2 min</td>
</tr>
<tr>
<td>223.205.189.164</td>
<td>5678</td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td>
<a href="/free-proxy-for-thailand/" title="泰国">
<img width="20" height="20" alt="泰国" src="/uploads/flag/thailand.svg">
TH </a>
</td>
<td>socks4</td>
<td>elite (HIA)</td>
<td>1 ms</td>
<td>97%</td>
<td>4104 ms</td>
<td>173 ms</td>
<td>8 d</td>
</tr>
<tr>
<td>110.78.186.220</td>
<td>4145</td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td>
<a href="/free-proxy-for-thailand/" title="泰国">
<img width="20" height="20" alt="泰国" src="/uploads/flag/thailand.svg">
TH </a>
</td>
<td>socks4</td>
<td>elite (HIA)</td>
<td>1 ms</td>
<td>62%</td>
<td>2796 ms</td>
<td>173 ms</td>
<td>8 d</td>
</tr>
<tr>
<td>101.109.48.54</td>
<td>8080</td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td>
<a href="/free-proxy-for-thailand/" title="泰国">
<img width="20" height="20" alt="泰国" src="/uploads/flag/thailand.svg">
TH </a>
</td>
<td>socks4</td>
<td>elite (HIA)</td>
<td>1 ms</td>
<td>75%</td>
<td>3782 ms</td>
<td>173 ms</td>
<td>2 min</td>
</tr>
<tr>
<td>171.6.78.164</td>
<td>8080</td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td>
<a href="/free-proxy-for-thailand/" title="泰国">
<img width="20" height="20" alt="泰国" src="/uploads/flag/thailand.svg">
TH </a>
</td>
<td>socks4</td>
<td>elite (HIA)</td>
<td>1 ms</td>
<td>100%</td>
<td>5006 ms</td>
<td>174 ms</td>
<td>2 min</td>
</tr>
<tr>
<td>134.236.30.92</td>
<td>5678</td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td>
<a href="/free-proxy-for-thailand/" title="泰国">
<img width="20" height="20" alt="泰国" src="/uploads/flag/thailand.svg">
TH </a>
</td>
<td>socks4</td>
<td>elite (HIA)</td>
<td>1 ms</td>
<td>59%</td>
<td>3696 ms</td>
<td>174 ms</td>
<td>8 d</td>
</tr>
<tr>
<td>118.173.230.19</td>
<td>1081</td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td><span>****</span> <img src="/assets/img/eye-off.svg" width="20" height="20" alt="hidden text" data-text="no need" class="js_openeyes"></td>
<td>
<a href="/free-proxy-for-thailand/" title="泰国">
<img width="20" height="20" alt="泰国" src="/uploads/flag/thailand.svg">
TH </a>
</td>
<td>socks5</td>
<td>elite (HIA)</td>
<td>1 ms</td>
<td>99%</td>
<td>3001 ms</td>
<td>174 ms</td>
<td>1 min</td>
</tr>
</tbody>
</table>
<ul class="pagination"><li class="disabled"><span>&laquo;</span></li> <li class="active"><span>1</span></li><li><a href="/free-proxy-for-thailand?page=2">2</a></li><li><a href="/free-proxy-for-thailand?page=3">3</a></li><li><a href="/free-proxy-for-thailand?page=4">4</a></li><li><a href="/free-proxy-for-thailand?page=5">5</a></li><li><a href="/free-proxy-for-thailand?page=6">6</a></li><li><a href="/free-proxy-for-thailand?page=7">7</a></li><li><a href="/free-proxy-for-thailand?page=8">8</a></li><li class="disabled"><span>...</span></li><li><a href="/free-proxy-for-thailand?page=15">15</a></li><li><a href="/free-proxy-for-thailand?page=16">16</a></li> <li><a href="/free-proxy-for-thailand?page=2">&raquo;</a></li></ul> </div>
<div class="row py-3 pb-5">
<h2>泰国代理服务器:解锁数字边界的智能钥匙</h2>
<h3>核心价值解析:为什么选择泰国代理?</h3>
<p>在全球化互联网时代地理限制成为了数字世界中的无形围墙。泰国代理服务器恰如一把智能钥匙为您打开这扇限制之门。通过隐藏真实IP地址它不仅能提升您的网络匿名性更为关键业务场景提供支持</p>
<ul>
<li>内容解锁专家:独家访问泰国本土流媒体平台、新闻网站和政府门户</li>
<li>数据采集利器:从泰国服务器获取精准的本地化数据,助力市场调研</li>
<li>隐私防护盾牌:为远程工作者和数字游民提供额外的安全层</li>
<li>网络研究工具:帮助学者研究泰国网络生态的特殊性</li>
</ul>
<h3>我们的技术优势:不只是免费</h3>
<p>我们提供的泰国代理服务之所以出众,源于三大技术创新:</p>
<ol>
<li>智能刷新系统采用AI算法每180秒动态更新代理池淘汰失效节点确保95%以上的可用率</li>
<li>协议矩阵支持:
<ul>
<li>HTTP(S):基础网页浏览的理想选择</li>
<li>SOCKS4平衡速度与兼容性</li>
<li>SOCKS5支持UDP和IPv6的进阶之选</li>
</ul>
</li>
<li>三维筛选引擎:可同时按延迟(ms)、匿名等级(1-3级)、在线率(%)进行精准筛选</li>
</ol>
<h3>性能优化建议</h3>
<p>根据实测数据,我们建议:</p>
<ul>
<li>视频流媒体:选择延迟<150ms的SOCKS5代理</li>
<li>大数据采集:使用住宅代理轮换池</li>
<li>即时通讯固定1-2个高匿名节点</li>
</ul>
<p>我们观察到一个有趣现象:莫斯科地区的代理在欧美时段(UTC+3 18:00-24:00)通常具有更好的国际带宽这或许与当地ISP的流量调度策略有关。</p>
<p>您是否知道泰国代理在应对某些国际CDN限制时表现出独特优势这源于其特殊的网络基础设施布局。尝试不同的节点位置可能会发现意外的连接优化效果。</p>
</div>
</div>
</main>
<footer>
<div class="footer-bottom py-3">
<div class="container text-center">
<p>
<a href="/">免费VPN节点</a> 版权所有. <br />
Powered by WordPress
</p>
</div>
</div>
</footer>
<script src="/assets/libs/jquery/dist/jquery.min.js"></script>
<script src="/assets/libs/bootstrap/dist/js/bootstrap.min.js"></script>
<script defer src="/assets/js/frontend/base.js"></script>
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v833ccba57c9e4d2798f2e76cebdd09a11778172276447" integrity="sha512-57MDmcccJXYtNnH+ZiBwzC4jb2rvgVCEokYN+L/nLlmO8rfYT/gIpW2A569iJ/3b+0UEasghjuZH/ma3wIs/EQ==" data-cf-beacon='{"version":"2024.11.0","token":"05bf4e6b284745fc9731cb17a9ae0ea4","r":1,"server_timing":{"name":{"cfCacheStatus":true,"cfEdge":true,"cfExtPri":true,"cfL4":true,"cfOrigin":true,"cfSpeedBrain":true},"location_startswith":null}}' crossorigin="anonymous"></script>
</body>
</html>

46
old/start.sh Normal file
View File

@ -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

248
old/开发文档.md Normal file
View File

@ -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上快速更改

1442
proxy.json Normal file

File diff suppressed because it is too large Load Diff

31
requirements.txt Normal file
View File

@ -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

172
scratch.py Normal file
View File

@ -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()

44
start.bat Normal file
View File

@ -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
)