refactor: enhance auto-login functionality and add MAC address randomization

This commit is contained in:
2026-04-13 12:48:46 -04:00
parent f50b7dab84
commit 955e636d5f
2 changed files with 231 additions and 57 deletions

BIN
README.md

Binary file not shown.

280
main.py
View File

@@ -1,74 +1,248 @@
from selenium import webdriver import argparse
from selenium.webdriver.common.by import By import random
from selenium.webdriver.support.ui import WebDriverWait import subprocess
from selenium.webdriver.support import expected_conditions as EC import sys
from selenium.webdriver.edge.service import Service as EdgeService import time
from selenium.webdriver.edge.options import Options
# 如果你安装了 webdriver_manager取消下面这行的注释
# from webdriver_manager.microsoft import EdgeChromiumDriverManager
import requests 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(): 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: try:
# 尝试访问一个极其稳定的网站,设置短超时 base_key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, base_path)
requests.get("https://www.google.com", timeout=3) except OSError:
return True return None
except:
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 return False
def auto_login_wifi(): print(f"找到 Wi-Fi 适配器: {adapter_name}")
# 配置 Edge 选项
edge_options = Options() reg_path = find_adapter_registry_key(adapter_name)
edge_options.add_argument("--start-maximized") # 最大化窗口 if not reg_path:
edge_options.add_argument("--ignore-certificate-errors") # 忽略证书错误公共Wi-Fi常见 print(f"错误:未在注册表中找到适配器 '{adapter_name}' 的条目。")
edge_options.add_argument("--headless") # 如果不想看到浏览器界面,取消注释这一行 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: try:
# 初始化 Edge 驱动 key = winreg.OpenKey(
# 如果安装了 webdriver_manager使用下面这行 winreg.HKEY_LOCAL_MACHINE, reg_path, 0, winreg.KEY_SET_VALUE,
# driver = webdriver.Edge(service=EdgeService(EdgeChromiumDriverManager().install()), options=edge_options) )
winreg.SetValueEx(key, "NetworkAddress", 0, winreg.REG_SZ, mac_str)
winreg.CloseKey(key)
except PermissionError:
print("错误:需要管理员权限来修改 MAC 地址。请以管理员身份运行。")
return False
# 如果没有安装 webdriver_manager且驱动已在环境变量中直接使用下面这行 print(f"正在将 MAC 地址更改为: {mac_display}")
driver = webdriver.Edge(options=edge_options)
print("正在打开浏览器并访问触发页面...") # 获取网络适配器的接口名称(用于 netsh
# 1. 访问触发页面,这会自动重定向到 OSU 的登录页 interface_name = get_netsh_interface_name()
driver.get("http://captive.apple.com/") if not interface_name:
interface_name = adapter_name
# 设置显式等待,最长等待 20 秒,确保页面元素加载出来 print("正在重启网卡以应用新 MAC 地址...")
wait = WebDriverWait(driver, 20) 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
print("等待跳转至登录页面...")
# 2. 定位并勾选 "I accept the terms of use" def get_netsh_interface_name():
# 使用 name="visitor_accept_terms" 定位,这比包含随机数字的 ID 更稳定 """获取 Wi-Fi 网络接口的名称(用于 netsh interface 命令)。"""
checkbox = wait.until(EC.element_to_be_clickable((By.NAME, "visitor_accept_terms"))) 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
if not checkbox.is_selected():
checkbox.click()
print("已勾选同意协议。")
# 3. 定位并点击 "Log In" 按钮 def auto_login_wifi():
# 同样避开动态 ID使用 XPath 查找 value 为 "Log In" 的提交按钮 """自动登录 OSU 公共 Wi-Fi支持重试。"""
login_button = driver.find_element(By.XPATH, "//input[@type='submit' and @value='Log In']") edge_options = Options()
login_button.click() edge_options.add_argument("--start-maximized")
print("已点击登录按钮。") edge_options.add_argument("--ignore-certificate-errors")
edge_options.add_argument("--headless")
# 保持窗口打开一小会儿以确认连接成功(根据需要调整) for attempt in range(1, MAX_LOGIN_RETRIES + 1):
import time driver = None
time.sleep(5) try:
print("操作完成。") driver = webdriver.Edge(options=edge_options)
print(f"{attempt} 次尝试登录...")
except Exception as e: # 依次尝试不同的 Captive Portal 触发 URL
print(f"发生错误: {e}") portal_reached = False
finally: for url in CAPTIVE_PORTAL_URLS:
# 如果你想让浏览器保持打开状态,注释掉下面这行 print(f" 访问触发页面: {url}")
driver.quit() 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 __name__ == "__main__":
if is_connected(): if is_connected():
print("网络已连接,无需登录。") print("网络已连接,无需登录。")
else: return
auto_login_wifi()
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()