知行合一1 分钟阅读
vxray节点 用Python通过xray_core检测
import requests
import json
import os
import subprocess
import time
import platform
import zipfile
import socket
import stat
from urllib.parse import urlparse, parse_qs
# --- 配置区域 ---
TEST_URL = "http://www.google.com/generate_204"
XRAY_PATH = "xray_core"
OUTPUT_FILE = "available_nodes.txt"
# --- 1. 智能识别系统环境 (已适配 Android) ---
uname_info = platform.uname()
system_name = platform.system().lower()
machine_name = platform.machine().lower()
print(f"📱 检测系统信息: {system_name} | 架构: {machine_name}")
IS_ANDROID = "android" in str(uname_info).lower() or "aarch64" in machine_name
IS_WINDOWS = "windows" in system_name
# 确定文件名和下载链接
if IS_WINDOWS:
XRAY_BIN_NAME = "xray.exe"
DOWNLOAD_ASSET = "Xray-windows-64.zip"
elif IS_ANDROID or "aarch64" in machine_name:
# 安卓手机通常是 arm64 架构
XRAY_BIN_NAME = "xray"
DOWNLOAD_ASSET = "Xray-linux-arm64-v8a.zip"
else:
# 默认 Linux x64 (PC/服务器)
XRAY_BIN_NAME = "xray"
DOWNLOAD_ASSET = "Xray-linux-64.zip"
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
XRAY_BIN = os.path.join(CURRENT_DIR, XRAY_PATH, XRAY_BIN_NAME)
OUTPUT_PATH = os.path.join(CURRENT_DIR, OUTPUT_FILE)
def get_free_port():
"""获取本地空闲端口"""
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
# 安卓 Termux 环境建议绑定 127.0.0.1
s.bind(('127.0.0.1', 0))
return s.getsockname()[1]
except:
import random
return random.randint(20000, 50000)
def install_xray():
"""下载并安装 Xray"""
if not os.path.exists(XRAY_PATH):
os.makedirs(XRAY_PATH)
if os.path.exists(XRAY_BIN):
if not IS_WINDOWS:
st = os.stat(XRAY_BIN)
os.chmod(XRAY_BIN, st.st_mode | stat.S_IEXEC)
return True
print(f"⬇️ 正在下载适配手机的核心: {DOWNLOAD_ASSET} ...")
# 增加加速源
urls = [
f"https://mirror.ghproxy.com/https://github.com/XTLS/Xray-core/releases/download/v1.8.4/{DOWNLOAD_ASSET}",
f"https://github.com/XTLS/Xray-core/releases/download/v1.8.4/{DOWNLOAD_ASSET}"
]
download_success = False
zip_path = "xray_temp.zip"
for url in urls:
try:
print(f"尝试下载: {url}")
headers = {'User-Agent': 'Mozilla/5.0'}
r = requests.get(url, headers=headers, stream=True, timeout=15)
if r.status_code == 200:
with open(zip_path, 'wb') as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
download_success = True
break
except Exception as e:
print(f"下载失败: {e}")
continue
if not download_success:
print("❌ 下载失败,请检查网络。")
return False
try:
print("📦 解压中...")
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
zip_ref.extractall(XRAY_PATH)
os.remove(zip_path)
if os.path.exists(XRAY_BIN):
if not IS_WINDOWS:
print("🔒 赋予执行权限...")
st = os.stat(XRAY_BIN)
os.chmod(XRAY_BIN, st.st_mode | stat.S_IEXEC)
return True
else:
print("❌ 解压后未找到文件")
return False
except Exception as e:
print(f"❌ 解压错误: {e}")
return False
def vless_to_config(node_str, local_port):
# (保持原有的转换逻辑不变)
try:
parsed = urlparse(node_str)
if not node_str.startswith("vless://"): return None
if '@' in parsed.netloc:
user_id, host_port = parsed.netloc.split('@', 1)
else: return None
if ':' in host_port:
address = host_port.rpartition(':')[0]
port = int(host_port.rpartition(':')[2])
else:
address, port = host_port, 443
qs = parse_qs(parsed.query)
security = qs.get('security', ['none'])[0]
type_network = qs.get('type', ['tcp'])[0]
sni = qs.get('sni', [''])[0]
host = qs.get('host', [''])[0]
path = qs.get('path', ['/'])[0]
fp = qs.get('fp', ['chrome'])[0]
pbk = qs.get('pbk', [''])[0]
sid = qs.get('sid', [''])[0]
service_name = qs.get('serviceName', [''])[0]
server_name = sni if sni else (host if host else address)
outbound = {
"protocol": "vless",
"settings": {
"vnext": [{"address": address, "port": port, "users": [{"id": user_id, "encryption": "none"}]}]
},
"streamSettings": {
"network": type_network,
"security": security
}
}
if security == 'tls':
outbound['streamSettings']['tlsSettings'] = {"serverName": server_name, "fingerprint": fp}
elif security == 'reality':
outbound['streamSettings']['realitySettings'] = {"serverName": server_name, "publicKey": pbk, "shortId": sid, "fingerprint": fp}
if type_network == 'ws':
outbound['streamSettings']['wsSettings'] = {"path": path, "headers": {"Host": host if host else server_name}}
elif type_network == 'grpc':
outbound['streamSettings']['grpcSettings'] = {"serviceName": service_name}
config = {
"log": {"loglevel": "none"},
"inbounds": [{"port": local_port, "protocol": "socks", "settings": {"auth": "noauth", "udp": True}}],
"outbounds": [outbound]
}
return config
except: return None
def check_node_real(node_str):
current_port = get_free_port()
config = vless_to_config(node_str, current_port)
if not config: return False, 0
config_path = os.path.join(XRAY_PATH, f"config_{current_port}.json")
with open(config_path, 'w') as f: json.dump(config, f)
process = None
try:
# 安卓端启动无需 startupinfo
process = subprocess.Popen([XRAY_BIN, "run", "-c", config_path],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
time.sleep(1.5) # 手机性能稍弱,给 1.5秒
proxies = {
'http': f'socks5h://127.0.0.1:{current_port}',
'https': f'socks5h://127.0.0.1:{current_port}'
}
start = time.time()
resp = requests.get(TEST_URL, proxies=proxies, timeout=5)
latency = time.time() - start
if resp.status_code in [200, 204]:
return True, latency
else:
return False, 0
except Exception:
return False, 0
finally:
if process:
try:
process.terminate()
process.wait(timeout=1)
except:
try: process.kill()
except: pass
if os.path.exists(config_path):
try: os.remove(config_path)
except: pass
def main(url_list):
if not install_xray(): return
print("\n📡 正在获取节点...")
all_nodes = []
for url in url_list:
try:
if url.startswith("http"): content = requests.get(url, timeout=10).text
else:
if os.path.exists(url):
with open(url, 'r', encoding='utf-8') as f: content = f.read()
else: continue
for line in content.split('\n'):
if line.strip().startswith("vless://"): all_nodes.append(line.strip())
except: pass
unique_nodes = list(set(all_nodes))
print(f"📝 共 {len(unique_nodes)} 个节点,结果将保存在: {OUTPUT_FILE}\n")
with open(OUTPUT_PATH, 'w', encoding='utf-8') as f: f.write("")
for i, node in enumerate(unique_nodes):
alias = node.split('#')[-1] if '#' in node else "无名"
print(f"[{i+1}] {alias[:10]:<10} ... ", end="", flush=True)
is_ok, latency = check_node_real(node)
if is_ok:
print(f"✅ {int(latency*1000)}ms")
with open(OUTPUT_PATH, 'a', encoding='utf-8') as f:
f.write(node + '\n')
else:
print(f"❌")
if __name__ == "__main__":
urls = ['https://xxxxxxx.txt']
main(urls)有关使用上的问题,欢迎您在底部评论区留言,一起交流~
读者评论
评论会同步写入该文在 Notion 中的页面底部(与正文同页,便于管理)。
暂无评论,欢迎抢沙发。