import argparse import random import subprocess import sys import time import requests import winreg from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.edge.options import Options from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import WebDriverWait # Captive Portal 触发 URL 列表,按优先级依次尝试 CAPTIVE_PORTAL_URLS = [ "http://captive.apple.com/", "http://www.msftconnecttest.com/redirect", "http://detectportal.firefox.com/", ] MAX_LOGIN_RETRIES = 3 def is_connected(): """通过 HTTP 204 检测网络是否连通。""" test_urls = [ ("http://clients3.google.com/generate_204", 204), ("http://www.msftconnecttest.com/connecttest.txt", 200), ] for url, expected_status in test_urls: try: r = requests.get(url, timeout=3) if r.status_code == expected_status: return True except Exception: continue return False def get_wifi_adapter_name(): """获取当前活跃的 Wi-Fi 网卡名称。""" result = subprocess.run( ["netsh", "wlan", "show", "interfaces"], capture_output=True, text=True, encoding="gbk", ) for line in result.stdout.splitlines(): line = line.strip() if line.startswith("名称") or line.startswith("Name"): return line.split(":", 1)[1].strip() return None def generate_random_mac(): """生成一个随机的本地管理单播 MAC 地址。""" mac = [random.randint(0x00, 0xFF) for _ in range(6)] mac[0] = (mac[0] & 0xFC) | 0x02 # 本地管理、单播 return mac def find_adapter_registry_key(adapter_name): """在注册表中找到匹配 adapter_name 的网卡子键路径。""" base_path = r"SYSTEM\CurrentControlSet\Control\Class\{4d36e972-e325-11ce-bfc1-08002be10318}" try: base_key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, base_path) except OSError: return None index = 0 while True: try: subkey_name = winreg.EnumKey(base_key, index) index += 1 except OSError: break try: subkey = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, f"{base_path}\\{subkey_name}") driver_desc, _ = winreg.QueryValueEx(subkey, "DriverDesc") if adapter_name.lower() in driver_desc.lower(): winreg.CloseKey(subkey) winreg.CloseKey(base_key) return f"{base_path}\\{subkey_name}" winreg.CloseKey(subkey) except OSError: continue winreg.CloseKey(base_key) return None def randomize_mac(): """随机化当前 Wi-Fi 网卡的 MAC 地址(需要管理员权限)。""" adapter_name = get_wifi_adapter_name() if not adapter_name: print("错误:未找到活跃的 Wi-Fi 适配器。") return False print(f"找到 Wi-Fi 适配器: {adapter_name}") reg_path = find_adapter_registry_key(adapter_name) if not reg_path: print(f"错误:未在注册表中找到适配器 '{adapter_name}' 的条目。") return False mac = generate_random_mac() mac_str = "".join(f"{b:02X}" for b in mac) mac_display = ":".join(f"{b:02X}" for b in mac) try: key = winreg.OpenKey( winreg.HKEY_LOCAL_MACHINE, reg_path, 0, winreg.KEY_SET_VALUE, ) winreg.SetValueEx(key, "NetworkAddress", 0, winreg.REG_SZ, mac_str) winreg.CloseKey(key) except PermissionError: print("错误:需要管理员权限来修改 MAC 地址。请以管理员身份运行。") return False print(f"正在将 MAC 地址更改为: {mac_display}") # 获取网络适配器的接口名称(用于 netsh) interface_name = get_netsh_interface_name() if not interface_name: interface_name = adapter_name print("正在重启网卡以应用新 MAC 地址...") subprocess.run( ["netsh", "interface", "set", "interface", interface_name, "disable"], capture_output=True, ) time.sleep(2) subprocess.run( ["netsh", "interface", "set", "interface", interface_name, "enable"], capture_output=True, ) time.sleep(3) print(f"MAC 地址已更改为: {mac_display}") return True def get_netsh_interface_name(): """获取 Wi-Fi 网络接口的名称(用于 netsh interface 命令)。""" result = subprocess.run( ["netsh", "interface", "show", "interface"], capture_output=True, text=True, encoding="gbk", ) for line in result.stdout.splitlines(): if "Wi-Fi" in line or "WLAN" in line or "Wireless" in line: parts = line.split() if len(parts) >= 4: return " ".join(parts[3:]) return None def auto_login_wifi(): """自动登录 OSU 公共 Wi-Fi,支持重试。""" edge_options = Options() edge_options.add_argument("--start-maximized") edge_options.add_argument("--ignore-certificate-errors") edge_options.add_argument("--headless") for attempt in range(1, MAX_LOGIN_RETRIES + 1): driver = None try: driver = webdriver.Edge(options=edge_options) print(f"第 {attempt} 次尝试登录...") # 依次尝试不同的 Captive Portal 触发 URL portal_reached = False for url in CAPTIVE_PORTAL_URLS: print(f" 访问触发页面: {url}") driver.get(url) try: wait = WebDriverWait(driver, 10) wait.until(EC.element_to_be_clickable((By.NAME, "visitor_accept_terms"))) portal_reached = True break except Exception: print(f" 未能通过 {url} 跳转到登录页,尝试下一个...") continue if not portal_reached: print(f" 第 {attempt} 次尝试未能到达登录页面。") continue # 勾选同意协议 checkbox = driver.find_element(By.NAME, "visitor_accept_terms") if not checkbox.is_selected(): checkbox.click() print(" 已勾选同意协议。") time.sleep(1) # 点击登录按钮 login_button = driver.find_element( By.XPATH, "//input[@type='submit' and @value='Log In']", ) login_button.click() print(" 已点击登录按钮。") # 轮询验证是否真正连上网络 for i in range(10): time.sleep(2) if is_connected(): print("登录成功!网络已连通。") return print(f" 第 {attempt} 次尝试:点击登录后网络仍未连通。") except Exception as e: print(f" 第 {attempt} 次尝试出错: {e}") finally: if driver: driver.quit() if attempt < MAX_LOGIN_RETRIES: print(" 等待 3 秒后重试...") time.sleep(3) print("多次尝试后仍未能成功登录。") def main(): parser = argparse.ArgumentParser(description="OSU 公共 Wi-Fi 自动登录工具") parser.add_argument( "--random-mac", action="store_true", help="登录前随机化 Wi-Fi 网卡的 MAC 地址(需要管理员权限)", ) args = parser.parse_args() if is_connected(): print("网络已连接,无需登录。") return if args.random_mac: print("正在随机化 MAC 地址...") if not randomize_mac(): print("MAC 地址随机化失败,继续尝试登录...") else: # MAC 更改后需要等待 Wi-Fi 重新连接 print("等待 Wi-Fi 重新连接...") time.sleep(5) auto_login_wifi() if __name__ == "__main__": main()