beta 1.0
This commit is contained in:
commit
366a84fa89
267
README.md
Normal file
267
README.md
Normal 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
17
config.yaml
Normal 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
16
core/__init__.py
Normal 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
219
core/data_source.py
Normal 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
192
core/health_checker.py
Normal 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
98
core/models.py
Normal 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
266
core/proxy_manager.py
Normal 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)
|
||||
385
core/simple_proxy_forwarder.py
Normal file
385
core/simple_proxy_forwarder.py
Normal 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
124
diagnose.py
Normal 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
4
gui/__init__.py
Normal file
@ -0,0 +1,4 @@
|
||||
"""GUI模块"""
|
||||
from .main_window import MainWindow
|
||||
|
||||
__all__ = ['MainWindow']
|
||||
BIN
gui/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
gui/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
gui/__pycache__/main_window.cpython-312.pyc
Normal file
BIN
gui/__pycache__/main_window.cpython-312.pyc
Normal file
Binary file not shown.
587
gui/main_window.py
Normal file
587
gui/main_window.py
Normal 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
34
local.json
Normal 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
307
logs/whereami.log
Normal 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
65
main.py
Normal 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
BIN
old/old_design.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 417 KiB |
739
old/page.html
Normal file
739
old/page.html
Normal 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>«</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">»</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
46
old/start.sh
Normal 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
248
old/开发文档.md
Normal 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
1442
proxy.json
Normal file
File diff suppressed because it is too large
Load Diff
31
requirements.txt
Normal file
31
requirements.txt
Normal 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
172
scratch.py
Normal 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
44
start.bat
Normal 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
|
||||
)
|
||||
Loading…
Reference in New Issue
Block a user