添加Webshare代理获取脚本和Clash配置

- 添加fetch_proxies.py脚本,支持从Webshare API获取代理
- 生成多种格式的代理文件:raw、http、socks5
- 生成Clash配置文件clash_config.yaml
- 包含代理组配置和分流规则
- 支持自动选择和故障转移
This commit is contained in:
WebShare Proxy Bot 2025-09-23 06:15:28 +00:00
parent 24963efa76
commit 064cbe1d5f
7 changed files with 721 additions and 0 deletions

23
.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
# 环境变量文件
.env
# Python缓存
__pycache__/
*.pyc
*.pyo
*.pyd
.Python
*.so
# IDE文件
.vscode/
.idea/
*.swp
*.swo
# 系统文件
.DS_Store
Thumbs.db
# 日志文件
*.log

227
clash_config.yaml Normal file
View File

@ -0,0 +1,227 @@
allow-lan: false
external-controller: 127.0.0.1:9090
log-level: info
mode: rule
port: 7890
proxies:
- name: proxy-1
password: lvo4zphp2wwj
port: 6269
server: 45.196.40.191
type: http
username: fbkjstyt
- name: proxy-2
password: lvo4zphp2wwj
port: 6452
server: 130.180.228.168
type: http
username: fbkjstyt
- name: proxy-3
password: lvo4zphp2wwj
port: 7926
server: 72.1.154.35
type: http
username: fbkjstyt
- name: proxy-4
password: lvo4zphp2wwj
port: 6479
server: 63.141.62.186
type: http
username: fbkjstyt
- name: proxy-5
password: lvo4zphp2wwj
port: 6219
server: 216.170.122.181
type: http
username: fbkjstyt
- name: proxy-6
password: lvo4zphp2wwj
port: 5758
server: 192.53.67.209
type: http
username: fbkjstyt
- name: proxy-7
password: lvo4zphp2wwj
port: 8160
server: 130.180.231.18
type: http
username: fbkjstyt
- name: proxy-8
password: lvo4zphp2wwj
port: 5936
server: 192.53.142.239
type: http
username: fbkjstyt
- name: proxy-9
password: lvo4zphp2wwj
port: 5686
server: 103.130.178.22
type: http
username: fbkjstyt
- name: proxy-10
password: lvo4zphp2wwj
port: 6563
server: 216.98.254.253
type: http
username: fbkjstyt
- name: proxy-11
password: lvo4zphp2wwj
port: 5896
server: 192.46.188.237
type: http
username: fbkjstyt
- name: proxy-12
password: lvo4zphp2wwj
port: 8932
server: 45.56.161.56
type: http
username: fbkjstyt
- name: proxy-13
password: lvo4zphp2wwj
port: 6766
server: 192.46.201.252
type: http
username: fbkjstyt
- name: proxy-14
password: lvo4zphp2wwj
port: 6384
server: 45.196.50.62
type: http
username: fbkjstyt
- name: proxy-15
password: lvo4zphp2wwj
port: 6363
server: 193.160.83.42
type: http
username: fbkjstyt
- name: proxy-16
password: lvo4zphp2wwj
port: 5898
server: 103.130.178.234
type: http
username: fbkjstyt
- name: proxy-17
password: lvo4zphp2wwj
port: 6622
server: 72.46.139.62
type: http
username: fbkjstyt
- name: proxy-18
password: lvo4zphp2wwj
port: 6799
server: 72.46.139.239
type: http
username: fbkjstyt
- name: proxy-19
password: lvo4zphp2wwj
port: 5726
server: 103.130.178.62
type: http
username: fbkjstyt
- name: proxy-20
password: lvo4zphp2wwj
port: 6247
server: 72.46.138.21
type: http
username: fbkjstyt
proxy-groups:
- name: 🚀 节点选择
proxies:
- ♻️ 自动选择
- 🎯 故障转移
- proxy-1
- proxy-2
- proxy-3
- proxy-4
- proxy-5
- proxy-6
- proxy-7
- proxy-8
- proxy-9
- proxy-10
- proxy-11
- proxy-12
- proxy-13
- proxy-14
- proxy-15
- proxy-16
- proxy-17
- proxy-18
- proxy-19
- proxy-20
type: select
- interval: 300
name: ♻️ 自动选择
proxies: &id001
- proxy-1
- proxy-2
- proxy-3
- proxy-4
- proxy-5
- proxy-6
- proxy-7
- proxy-8
- proxy-9
- proxy-10
- proxy-11
- proxy-12
- proxy-13
- proxy-14
- proxy-15
- proxy-16
- proxy-17
- proxy-18
- proxy-19
- proxy-20
type: url-test
url: http://www.gstatic.com/generate_204
- interval: 300
name: 🎯 故障转移
proxies: *id001
type: fallback
url: http://www.gstatic.com/generate_204
- name: 🌍 国外媒体
proxies:
- 🚀 节点选择
- ♻️ 自动选择
- 🎯 故障转移
- proxy-1
- proxy-2
- proxy-3
- proxy-4
- proxy-5
- proxy-6
- proxy-7
- proxy-8
- proxy-9
- proxy-10
- proxy-11
- proxy-12
- proxy-13
- proxy-14
- proxy-15
- proxy-16
- proxy-17
- proxy-18
- proxy-19
- proxy-20
type: select
- name: 🍃 应用净化
proxies:
- REJECT
- DIRECT
type: select
rules:
- DOMAIN-SUFFIX,google.com,🚀 节点选择
- DOMAIN-SUFFIX,youtube.com,🌍 国外媒体
- DOMAIN-SUFFIX,facebook.com,🌍 国外媒体
- DOMAIN-SUFFIX,twitter.com,🌍 国外媒体
- DOMAIN-SUFFIX,instagram.com,🌍 国外媒体
- DOMAIN-SUFFIX,netflix.com,🌍 国外媒体
- DOMAIN-KEYWORD,google,🚀 节点选择
- DOMAIN-KEYWORD,youtube,🌍 国外媒体
- DOMAIN-KEYWORD,facebook,🌍 国外媒体
- DOMAIN-KEYWORD,github,🚀 节点选择
- GEOIP,CN,DIRECT
- MATCH,🚀 节点选择
socks-port: 7891

410
fetch_proxies.py Normal file
View File

@ -0,0 +1,410 @@
#!/usr/bin/env python3
"""
Webshare SOCKS代理获取脚本
使用简单的下载接口直接获取代理列表
"""
import os
import requests
import yaml
from typing import Optional, List, Dict
def load_api_key() -> Optional[str]:
"""
.env文件或环境变量中加载API密钥
Returns:
API密钥或None
"""
# 首先尝试从.env文件读取
env_file = ".env"
if os.path.exists(env_file):
try:
with open(env_file, 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if not line or line.startswith('#'):
continue
if '=' in line:
# 处理 TOKEN=xxx 格式
key, value = line.split('=', 1)
if key.strip() == 'TOKEN':
return value.strip()
else:
# 如果第一行不包含=则认为是直接的token
return line
except Exception as e:
print(f"读取.env文件时出错: {e}")
# 然后尝试从环境变量读取
api_key = os.getenv('TOKEN') or os.getenv('WEBSHARE_API_KEY')
if api_key:
return api_key.strip()
return None
def get_download_token(api_key: str) -> Optional[str]:
"""
获取下载token
Args:
api_key: API密钥
Returns:
下载token或None
"""
url = "https://proxy.webshare.io/api/v2/download_token/proxy_list/"
headers = {
"Authorization": f"Token {api_key}",
"Content-Type": "application/json"
}
try:
print("🔑 正在获取下载token...")
response = requests.post(url, headers=headers)
if response.status_code == 200:
data = response.json()
download_token = data.get("key") # API返回的字段是'key'而不是'download_token'
if download_token:
print("✅ 成功获取下载token")
return download_token
else:
print("❌ 响应中未找到下载token")
print(f"🔍 可用的键: {list(data.keys())}")
return None
else:
print(f"❌ 获取下载token失败 {response.status_code}: {response.text}")
return None
except requests.exceptions.RequestException as e:
print(f"获取下载token时出错: {e}")
return None
def download_proxies(download_token: str, country: str = "-", auth_method: str = "username",
endpoint_mode: str = "direct", location: str = ""):
"""
使用下载token获取代理列表
Args:
download_token: 下载token (不是API key)
country: 国家代码 (- 表示任意国家)
auth_method: 认证方式 (username ipauth)
endpoint_mode: 端点模式 (direct backbone)
location: 位置过滤 (可选)
Returns:
代理列表文本
"""
# 构建下载URL - 正确的格式
if location:
# URL编码位置参数
location_encoded = requests.utils.quote(location)
url = f"https://proxy.webshare.io/api/v2/proxy/list/download/{download_token}/{country}/any/{auth_method}/{endpoint_mode}/{location_encoded}/"
else:
# 没有位置过滤时,不包含最后的路径参数
url = f"https://proxy.webshare.io/api/v2/proxy/list/download/{download_token}/{country}/any/{auth_method}/{endpoint_mode}/"
try:
print(f"📡 正在从下载接口获取代理: {url}")
response = requests.get(url)
# 检查响应状态
if response.status_code == 404:
print("❌ 404错误 - 可能是URL格式不正确或token无效")
return None
elif response.status_code == 401:
print("❌ 401错误 - 下载token无效")
return None
elif response.status_code != 200:
print(f"❌ HTTP错误 {response.status_code}: {response.text}")
return None
# 检查是否返回了错误信息
if "invalid" in response.text.lower() or "error" in response.text.lower():
print(f"❌ API返回错误: {response.text}")
return None
return response.text
except requests.exceptions.RequestException as e:
print(f"下载代理列表时出错: {e}")
return None
def format_proxy_list(proxy_text: str, output_format: str = "socks5") -> list:
"""
格式化代理列表
Args:
proxy_text: 原始代理文本
output_format: 输出格式 (socks5, http, raw)
Returns:
格式化的代理列表
"""
if not proxy_text:
return []
lines = proxy_text.strip().split('\n')
formatted_proxies = []
for line in lines:
line = line.strip()
if not line:
continue
# 假设格式为: ip:port:username:password
parts = line.split(':')
if len(parts) >= 4:
ip, port, username, password = parts[0], parts[1], parts[2], parts[3]
if output_format == "socks5":
formatted_proxy = f"socks5://{username}:{password}@{ip}:{port}"
elif output_format == "http":
formatted_proxy = f"http://{username}:{password}@{ip}:{port}"
else: # raw
formatted_proxy = line
formatted_proxies.append(formatted_proxy)
elif len(parts) >= 2:
# 无认证格式: ip:port
ip, port = parts[0], parts[1]
if output_format == "socks5":
formatted_proxy = f"socks5://{ip}:{port}"
elif output_format == "http":
formatted_proxy = f"http://{ip}:{port}"
else: # raw
formatted_proxy = line
formatted_proxies.append(formatted_proxy)
return formatted_proxies
def save_proxies_to_file(proxies: list, filename: str = "socks_proxies.txt"):
"""
保存代理列表到文件
Args:
proxies: 代理列表
filename: 文件名
"""
try:
with open(filename, 'w', encoding='utf-8') as f:
for proxy in proxies:
f.write(f"{proxy}\n")
print(f"✅ 代理列表已保存到 {filename}")
except Exception as e:
print(f"❌ 保存文件时出错: {e}")
def parse_proxies(proxy_text: str) -> list:
"""
解析代理文本提取代理信息
Args:
proxy_text: 代理文本
Returns:
代理信息列表
"""
proxies = []
lines = proxy_text.strip().split('\n')
for line in lines:
line = line.strip()
if not line:
continue
# 解析格式: host:port:username:password
parts = line.split(':')
if len(parts) >= 4:
proxy = {
'host': parts[0],
'port': parts[1],
'username': parts[2],
'password': ':'.join(parts[3:]) # 密码可能包含冒号
}
proxies.append(proxy)
return proxies
def save_proxies(proxies: list, filename: str, format_type: str = "raw"):
"""
保存代理列表到文件
Args:
proxies: 代理列表
filename: 文件名
format_type: 格式类型 ("raw", "http", "socks5")
"""
with open(filename, 'w', encoding='utf-8') as f:
for proxy in proxies:
if format_type == "raw":
# 原始格式: host:port:username:password
line = f"{proxy['host']}:{proxy['port']}:{proxy['username']}:{proxy['password']}"
elif format_type == "http":
# HTTP格式: http://username:password@host:port
line = f"http://{proxy['username']}:{proxy['password']}@{proxy['host']}:{proxy['port']}"
elif format_type == "socks5":
# SOCKS5格式: socks5://username:password@host:port
line = f"socks5://{proxy['username']}:{proxy['password']}@{proxy['host']}:{proxy['port']}"
else:
line = f"{proxy['host']}:{proxy['port']}:{proxy['username']}:{proxy['password']}"
f.write(line + '\n')
print(f"📄 已保存 {len(proxies)} 个代理到 {filename} ({format_type}格式)")
def generate_clash_config(proxies: List[Dict[str, str]]) -> Dict:
"""
生成Clash配置文件
Args:
proxies: 代理列表
Returns:
Clash配置字典
"""
clash_proxies = []
proxy_names = []
for i, proxy in enumerate(proxies):
proxy_name = f"proxy-{i+1}"
proxy_names.append(proxy_name)
# 创建HTTP代理配置
clash_proxy = {
"name": proxy_name,
"type": "http",
"server": proxy['host'],
"port": int(proxy['port']),
"username": proxy['username'],
"password": proxy['password']
}
clash_proxies.append(clash_proxy)
# 创建完整的Clash配置
clash_config = {
"port": 7890,
"socks-port": 7891,
"allow-lan": False,
"mode": "rule",
"log-level": "info",
"external-controller": "127.0.0.1:9090",
"proxies": clash_proxies,
"proxy-groups": [
{
"name": "🚀 节点选择",
"type": "select",
"proxies": ["♻️ 自动选择", "🎯 故障转移"] + proxy_names
},
{
"name": "♻️ 自动选择",
"type": "url-test",
"proxies": proxy_names,
"url": "http://www.gstatic.com/generate_204",
"interval": 300
},
{
"name": "🎯 故障转移",
"type": "fallback",
"proxies": proxy_names,
"url": "http://www.gstatic.com/generate_204",
"interval": 300
},
{
"name": "🌍 国外媒体",
"type": "select",
"proxies": ["🚀 节点选择", "♻️ 自动选择", "🎯 故障转移"] + proxy_names
},
{
"name": "🍃 应用净化",
"type": "select",
"proxies": ["REJECT", "DIRECT"]
}
],
"rules": [
"DOMAIN-SUFFIX,google.com,🚀 节点选择",
"DOMAIN-SUFFIX,youtube.com,🌍 国外媒体",
"DOMAIN-SUFFIX,facebook.com,🌍 国外媒体",
"DOMAIN-SUFFIX,twitter.com,🌍 国外媒体",
"DOMAIN-SUFFIX,instagram.com,🌍 国外媒体",
"DOMAIN-SUFFIX,netflix.com,🌍 国外媒体",
"DOMAIN-KEYWORD,google,🚀 节点选择",
"DOMAIN-KEYWORD,youtube,🌍 国外媒体",
"DOMAIN-KEYWORD,facebook,🌍 国外媒体",
"DOMAIN-KEYWORD,github,🚀 节点选择",
"GEOIP,CN,DIRECT",
"MATCH,🚀 节点选择"
]
}
return clash_config
def save_clash_config(proxies: List[Dict[str, str]], filename: str = "clash_config.yaml"):
"""
保存Clash配置文件
Args:
proxies: 代理列表
filename: 文件名
"""
clash_config = generate_clash_config(proxies)
try:
with open(filename, 'w', encoding='utf-8') as f:
yaml.dump(clash_config, f, default_flow_style=False, allow_unicode=True, indent=2)
print(f"📄 已保存Clash配置到 {filename}")
except Exception as e:
print(f"❌ 保存Clash配置时出错: {e}")
def main():
"""主函数"""
print("🚀 开始获取Webshare代理列表...")
# 加载API密钥
api_key = load_api_key()
if not api_key:
print("❌ 无法加载API密钥")
return
# 获取下载token
download_token = get_download_token(api_key)
if not download_token:
print("❌ 获取下载token失败")
return
# 下载代理列表
proxy_text = download_proxies(download_token)
if not proxy_text:
print("❌ 获取代理列表失败")
return
# 解析代理
proxies = parse_proxies(proxy_text)
if not proxies:
print("❌ 未找到有效的代理")
return
print(f"✅ 成功获取 {len(proxies)} 个代理")
# 保存原始代理列表
save_proxies(proxies, "proxies_raw.txt", format_type="raw")
# 保存HTTP格式代理列表
save_proxies(proxies, "proxies_http.txt", format_type="http")
# 保存SOCKS5格式代理列表
save_proxies(proxies, "proxies_socks5.txt", format_type="socks5")
# 生成Clash配置文件
save_clash_config(proxies, "clash_config.yaml")
print("🎉 代理列表已保存完成!")
print("📁 文件说明:")
print(" - proxies_raw.txt: 原始格式 (host:port:username:password)")
print(" - proxies_http.txt: HTTP格式 (http://username:password@host:port)")
print(" - proxies_socks5.txt: SOCKS5格式 (socks5://username:password@host:port)")
print(" - clash_config.yaml: Clash配置文件")
if __name__ == "__main__":
main()

1
pr.sh Normal file
View File

@ -0,0 +1 @@
curl "https://proxy.webshare.io/api/v2/proxy/list/download/ulowafkljtigyvuxlitaakdaqvozmcjrnnhwegdf/-/any/username/direct/san%20francisco/"

20
proxies_http.txt Normal file
View File

@ -0,0 +1,20 @@
http://fbkjstyt:lvo4zphp2wwj@45.196.40.191:6269
http://fbkjstyt:lvo4zphp2wwj@130.180.228.168:6452
http://fbkjstyt:lvo4zphp2wwj@72.1.154.35:7926
http://fbkjstyt:lvo4zphp2wwj@63.141.62.186:6479
http://fbkjstyt:lvo4zphp2wwj@216.170.122.181:6219
http://fbkjstyt:lvo4zphp2wwj@192.53.67.209:5758
http://fbkjstyt:lvo4zphp2wwj@130.180.231.18:8160
http://fbkjstyt:lvo4zphp2wwj@192.53.142.239:5936
http://fbkjstyt:lvo4zphp2wwj@103.130.178.22:5686
http://fbkjstyt:lvo4zphp2wwj@216.98.254.253:6563
http://fbkjstyt:lvo4zphp2wwj@192.46.188.237:5896
http://fbkjstyt:lvo4zphp2wwj@45.56.161.56:8932
http://fbkjstyt:lvo4zphp2wwj@192.46.201.252:6766
http://fbkjstyt:lvo4zphp2wwj@45.196.50.62:6384
http://fbkjstyt:lvo4zphp2wwj@193.160.83.42:6363
http://fbkjstyt:lvo4zphp2wwj@103.130.178.234:5898
http://fbkjstyt:lvo4zphp2wwj@72.46.139.62:6622
http://fbkjstyt:lvo4zphp2wwj@72.46.139.239:6799
http://fbkjstyt:lvo4zphp2wwj@103.130.178.62:5726
http://fbkjstyt:lvo4zphp2wwj@72.46.138.21:6247

20
proxies_raw.txt Normal file
View File

@ -0,0 +1,20 @@
45.196.40.191:6269:fbkjstyt:lvo4zphp2wwj
130.180.228.168:6452:fbkjstyt:lvo4zphp2wwj
72.1.154.35:7926:fbkjstyt:lvo4zphp2wwj
63.141.62.186:6479:fbkjstyt:lvo4zphp2wwj
216.170.122.181:6219:fbkjstyt:lvo4zphp2wwj
192.53.67.209:5758:fbkjstyt:lvo4zphp2wwj
130.180.231.18:8160:fbkjstyt:lvo4zphp2wwj
192.53.142.239:5936:fbkjstyt:lvo4zphp2wwj
103.130.178.22:5686:fbkjstyt:lvo4zphp2wwj
216.98.254.253:6563:fbkjstyt:lvo4zphp2wwj
192.46.188.237:5896:fbkjstyt:lvo4zphp2wwj
45.56.161.56:8932:fbkjstyt:lvo4zphp2wwj
192.46.201.252:6766:fbkjstyt:lvo4zphp2wwj
45.196.50.62:6384:fbkjstyt:lvo4zphp2wwj
193.160.83.42:6363:fbkjstyt:lvo4zphp2wwj
103.130.178.234:5898:fbkjstyt:lvo4zphp2wwj
72.46.139.62:6622:fbkjstyt:lvo4zphp2wwj
72.46.139.239:6799:fbkjstyt:lvo4zphp2wwj
103.130.178.62:5726:fbkjstyt:lvo4zphp2wwj
72.46.138.21:6247:fbkjstyt:lvo4zphp2wwj

20
proxies_socks5.txt Normal file
View File

@ -0,0 +1,20 @@
socks5://fbkjstyt:lvo4zphp2wwj@45.196.40.191:6269
socks5://fbkjstyt:lvo4zphp2wwj@130.180.228.168:6452
socks5://fbkjstyt:lvo4zphp2wwj@72.1.154.35:7926
socks5://fbkjstyt:lvo4zphp2wwj@63.141.62.186:6479
socks5://fbkjstyt:lvo4zphp2wwj@216.170.122.181:6219
socks5://fbkjstyt:lvo4zphp2wwj@192.53.67.209:5758
socks5://fbkjstyt:lvo4zphp2wwj@130.180.231.18:8160
socks5://fbkjstyt:lvo4zphp2wwj@192.53.142.239:5936
socks5://fbkjstyt:lvo4zphp2wwj@103.130.178.22:5686
socks5://fbkjstyt:lvo4zphp2wwj@216.98.254.253:6563
socks5://fbkjstyt:lvo4zphp2wwj@192.46.188.237:5896
socks5://fbkjstyt:lvo4zphp2wwj@45.56.161.56:8932
socks5://fbkjstyt:lvo4zphp2wwj@192.46.201.252:6766
socks5://fbkjstyt:lvo4zphp2wwj@45.196.50.62:6384
socks5://fbkjstyt:lvo4zphp2wwj@193.160.83.42:6363
socks5://fbkjstyt:lvo4zphp2wwj@103.130.178.234:5898
socks5://fbkjstyt:lvo4zphp2wwj@72.46.139.62:6622
socks5://fbkjstyt:lvo4zphp2wwj@72.46.139.239:6799
socks5://fbkjstyt:lvo4zphp2wwj@103.130.178.62:5726
socks5://fbkjstyt:lvo4zphp2wwj@72.46.138.21:6247