import requests import base64 from concurrent.futures import ThreadPoolExecutor, as_completed from collections import defaultdict from urllib.parse import quote # ================== 配置 ================== URL = "https://serverlist.piaservers.net/shadow_socks" THREADS = 5 TOTAL_REQUESTS = 400 # 建议 400~800 次,增加获取更多IP概率 TIMEOUT = 10 # ========================================= def fetch_once(): try: r = requests.get(URL, timeout=TIMEOUT, headers={ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" }) r.raise_for_status() text = r.text.strip() # 处理 "JSON\nbase64" 这种情况,取第一行(JSON部分) if '\n' in text: json_part = text.split('\n', 1)[0].strip() else: json_part = text data = json.loads(json_part) # 只解析JSON部分 if not isinstance(data, list): return None return data except Exception: return None print(f"开始使用 {THREADS} 个线程采集 {TOTAL_REQUESTS} 次...") servers = defaultdict(set) # region -> set of hosts with ThreadPoolExecutor(max_workers=THREADS) as executor: futures = [executor.submit(fetch_once) for _ in range(TOTAL_REQUESTS)] for i, future in enumerate(as_completed(futures), 1): data = future.result() if data: for item in data: if isinstance(item, dict): region = str(item.get("region", "")).strip() host = str(item.get("host", "")).strip() if region and host: servers[region].add(host) if i % 50 == 0 or i == TOTAL_REQUESTS: current_count = sum(len(h) for h in servers.values()) print(f"已完成 {i}/{TOTAL_REQUESTS} 次请求... 当前发现节点数: {current_count}") print("\n采集完成!") total = sum(len(hosts) for hosts in servers.values()) print(f"共发现 {total} 个独特节点\n") # 输出结果 print("地区 Host数量 Hosts") print("-" * 80) all_nodes = [] for region in sorted(servers): hosts = sorted(servers[region]) print(f"{region:<20} {len(hosts):<8} {', '.join(hosts)}") for host in hosts: all_nodes.append({ "region": region, "host": host, "port": 443, "password": "shadowsocks", "cipher": "aes-128-gcm" }) if not all_nodes: print("还是采集不到节点,请把下面这行贴给我:") print("你的网络环境 / 是否用了代理 / 是否在路由器/LEDE/OpenWrt 上运行") else: print(f"\n=== ss:// 节点链接(共 {len(all_nodes)} 个)===") ss_links = [] for node in all_nodes: auth = f"{node['cipher']}:{node['password']}@{node['host']}:{node['port']}" b64 = base64.urlsafe_b64encode(auth.encode()).decode().rstrip("=") name = f"PIA-{node['region']}-{node['host'].split('.')[-1]}" ss_url = f"ss://{b64}#{quote(name)}" ss_links.append(ss_url) print(ss_url) print(f"\n=== 一键订阅链接 ===") sub_url = "https://api.ss-sub.com/sub?target=ss&url=" + "|".join(ss_links) print(sub_url) # 保存文件 with open("pia_shadowsocks_nodes.txt", "w", encoding="utf-8") as f: f.write("\n".join(ss_links)) f.write(f"\n\n订阅链接:\n{sub_url}") print("\n节点已保存到 pia_shadowsocks_nodes.txt")