校园网自动重连工具
2025/11/5大约 9 分钟
校园网自动重连工具
问题背景:在课室远程连接宿舍电脑时,宿舍电脑的校园网经常掉线,导致远程桌面突然断开、搞得一上午都不能远程连接。为此,我开发了一套本地化、无需联网下载驱动的校园网自动重连守护脚本,采用模块化设计,支持网络状态分层检测、会话失效自动重建、配置灵活修改,能够后台静默运行,确保远程连接永不掉线。
一、工具核心优势与技术选型
1. 解决的核心问题
- 无需手动登录:断连后自动检测、自动重试,全程无需干预
- 本地化运行:驱动从本地路径加载,不依赖网络下载,适配无外网环境
- 分层网络检测:先判断是否连校园网,再判断是否已登录,避免无效操作
- 会话失效兜底:浏览器会话断开时自动重建驱动,不报错、不中断
- 配置与代码分离:账号、URL、检测间隔等都在JSON中,改配置不用改代码
2. 技术栈选型
| 模块 | 技术/工具 | 选型原因 |
|---|---|---|
| 浏览器自动化 | Selenium + EdgeDriver | Edge浏览器兼容性好,无头模式可后台运行,适合长期驻留 |
| 驱动管理 | 本地EdgeDriver | 避免联网依赖,手动指定路径更稳定,适合校园网环境 |
| 配置管理 | JSON | 轻量易读,支持动态修改账号、检测间隔等参数,无需重新打包 |
| 网络检测 | subprocess(调用系统ping命令) | 直接调用系统底层ping,比Python第三方库更稳定,适配Windows/Linux双系统 |
| 日志记录 | logging模块 | 实时记录网络状态、登录结果,方便排查问题(如登录失败原因) |
二、项目结构设计(模块化架构)
项目采用清晰的模块化结构,各模块职责明确,易于维护和扩展:
CampusNetworkAutoLogin/ # 项目根目录
├─ src/ # 源代码目录
│ ├─ config/ # 配置模块
│ │ ├── loader_config.py # 配置加载器
│ │ └── log_config.py # 日志配置
│ ├─ utils/ # 工具模块
│ │ ├── driver.py # 浏览器驱动初始化
│ │ └── network.py # 网络检测工具
│ ├─ services/ # 业务逻辑模块
│ │ ├── login.py # 登录核心逻辑
│ │ └── retry.py # 重试机制
│ └── main.py # 主程序入口
├─ resource/ # 配置文件目录
│ └── config.json # 配置文件(账号、URL、驱动路径等)
├─ logs/ # 日志文件目录(自动创建)
└─ requirements.txt # 依赖列表模块职责说明
| 模块 | 主要职责 | 文件位置 | |
|---|---|---|---|
| 配置管理 | 加载和解析配置文件 | src/config/loader_config.py | |
| 日志系统 | 初始化日志记录器,管理日志文件 | src/config/log_config.py | |
| 驱动管理 | 初始化Edge浏览器驱动 | src/utils/driver.py | |
| 网络检测 | 检测网络连接状态,支持多平台 | src/utils/network.py | |
| 登录服务 | 校园网登录核心逻辑 | src/services/login.py | |
| 重试机制 | 处理登录失败和会话失效的重试逻辑 | src/services/retry.py | |
| 主程序 | 程序入口,协调各模块工作 | src/main.py |
三、完整代码解析
1. 配置文件:resource/config.json
所有易变参数都集中在配置文件中,方便修改:
{
"username": "2023001234", // 校园网账号(学号/工号)
"password": "Campus123!", // 校园网密码
"campus_login_url": "http://10.0.0.1/login", // 校园网登录页URL
"online_flag": "已在线", // 登录成功的特征文字
"campus_intranet_ip": "10.0.0.1", // 校园网内网IP(能ping通表示已连接校园网)
"extranet_test_domain": "www.baidu.com", // 外网测试地址
"test_timeout": 5, // ping测试超时时间(秒)
"test_count": 2, // ping测试次数
"check_interval": 60, // 网络状态检测间隔(秒)
"retry_limit": 3, // 登录失败最大重试次数
"auth_id": "authBtn", // 统一身份认证按钮ID
"username_id": "usernameInput", // 账号输入框ID
"password_id": "passwordInput", // 密码输入框ID
"account_btn_xpath": "//div[text()='账号登录']", // 账号登录按钮XPATH
"login_btn_xpath": "//button[@type='submit']", // 登录按钮XPATH
"driver_path": "F:\\edgedriver_win64\\msedgedriver.exe", // EdgeDriver路径
"logging_level": "INFO" // 日志级别(DEBUG/INFO/WARNING/ERROR)
}2. 模块化代码解析
项目采用模块化设计,每个模块负责特定功能,代码更易维护和扩展。以下是各核心模块的功能和实现:
主程序入口(src/main.py)
主程序负责协调各模块工作,实现网络状态检测和自动登录的主循环:
import logging
import time
from config.log_config import init_logger
from utils.driver import init_edge_driver
from utils.network import check_network_status
from services.retry import retry_login
from config.loader_config import (
CHECK_INTERVAL, USERNAME, CAMPUS_LOGIN_URL
)
def main():
# 初始化日志系统
logging = init_logger()
"""主程序:循环检测网络状态,未登录时自动登录"""
logging.info(f"=== 校园网自动登录工具 v1.0 ===")
logging.info(f"检测间隔:{CHECK_INTERVAL}秒 | 登录页:{CAMPUS_LOGIN_URL}")
logging.info(f"当前账号:{USERNAME} | 启动时间:{time.strftime('%Y-%m-%d %H:%M:%S')}")
logging.info("=" * 50)
driver = init_edge_driver()
if not driver:
logging.error("Edge 浏览器驱动初始化失败,请检查驱动文件是否正确")
return
try:
while True:
status = check_network_status()
logging.info(f"网络状态:{status}")
if status == "未连接到校园网(请检查Wi-Fi/网线连接)":
logging.error("未连接到校园网,请检查Wi-Fi/网线连接")
break
if status == "已连接校园网,但未登录(需登录后访问外网)":
if not retry_login(driver):
logging.error("登录失败,程序终止")
break
time.sleep(CHECK_INTERVAL)
except KeyboardInterrupt:
logging.info("工具已手动退出")
finally:
driver.quit()
if __name__ == "__main__":
main()配置加载器(src/config/loader_config.py)
负责加载和解析配置文件,支持不同运行环境:
import json
import logging
import os
import sys
CONFIG_PATH = os.path.join("resource", "config.json")
def load_config():
"""
读取配置文件(config.json),支持打包和未打包两种环境
"""
# 判断是否为打包环境
if getattr(sys, "frozen", False):
root_dir = os.path.dirname(sys.executable)
else:
# 未打包时,从当前文件路径推算项目根目录
current_file_dir = os.path.dirname(os.path.abspath(__file__))
root_dir = os.path.dirname(os.path.dirname(current_file_dir))
full_config_path = os.path.join(root_dir, CONFIG_PATH)
# 检查配置文件是否存在
if not os.path.exists(full_config_path):
logging.error(f"配置文件缺失!请确保路径正确:{full_config_path}")
sys.exit(1)
with open(full_config_path, "r", encoding="utf-8") as f:
config = json.load(f)
return config
# 加载配置
config = load_config()
# 导出配置变量
USERNAME = config["username"]
PASSWORD = config["password"]
CAMPUS_LOGIN_URL = config["campus_login_url"]
ONLINE_FLAG = config["online_flag"]
CAMPUS_INTRANET_IP = config["campus_intranet_ip"]
EXTRANET_TEST_DOMAIN = config["extranet_test_domain"]
TEST_TIMEOUT = config["test_timeout"]
TEST_COUNT = config["test_count"]
CHECK_INTERVAL = config["check_interval"]
RETRY_LIMIT = config["retry_limit"]
AUTH_ID = config["auth_id"]
USERNAME_ID = config["username_id"]
PASSWORD_ID = config["password_id"]
ACCOUNT_BTN_XPATH = config["account_btn_xpath"]
LOGIN_BTN_XPATH = config["login_btn_xpath"]
LOGGING_LEVEL = config["logging_level"]网络检测工具(src/utils/network.py)
实现跨平台的网络状态检测功能:
import subprocess
import logging
import platform
from config.loader_config import CAMPUS_INTRANET_IP, EXTRANET_TEST_DOMAIN, TEST_TIMEOUT, TEST_COUNT
def ping_test(target: str, count: int = 2, timeout: int = 5) -> bool:
"""
使用系统 ping 命令检测目标是否可达,支持 Windows/Linux/macOS
"""
system_name = platform.system().lower()
# 根据操作系统选择命令参数
if system_name.startswith("win"):
# Windows 的 ping 超时单位是毫秒
command = ["ping", "-n", str(count), "-w", str(timeout * 1000), target]
else:
# Linux/macOS 的 ping 参数
command = ["ping", "-c", str(count), "-W", str(timeout), target]
try:
result = subprocess.run(
command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
timeout=timeout + 2
)
return result.returncode == 0
except Exception as e:
logging.error(f"执行 ping 时发生错误:{e}")
return False
def check_network_status() -> str:
"""
分层检测网络状态:
1. 先检测是否连接到校园网(ping内网IP)
2. 再检测是否已登录(ping外网)
"""
if not ping_test(CAMPUS_INTRANET_IP, TEST_COUNT, TEST_TIMEOUT):
return "未连接到校园网(请检查Wi-Fi/网线连接)"
if ping_test(EXTRANET_TEST_DOMAIN, TEST_COUNT, TEST_TIMEOUT):
return "网络正常(已登录校园网,可访问外网)"
else:
return "已连接校园网,但未登录(需登录后访问外网)"浏览器驱动管理(src/utils/driver.py)
负责初始化Edge浏览器驱动:
import os
import logging
from selenium.webdriver import Edge
from selenium.webdriver.edge.service import Service
from selenium.webdriver.edge.options import Options
from config.loader_config import config
def init_edge_driver():
"""
初始化Edge浏览器驱动,使用无头模式
"""
edge_options = Options()
edge_options.add_argument('--headless')
edge_options.add_argument('--disable-gpu')
edge_options.add_experimental_option('excludeSwitches', ['enable-logging'])
DRIVER_PATH = config["driver_path"]
if not os.path.exists(DRIVER_PATH):
logging.error(f"EdgeDriver 不存在,请检查路径是否正确:{DRIVER_PATH}")
return None
try:
service = Service(DRIVER_PATH)
driver = Edge(service=service, options=edge_options)
driver.implicitly_wait(30)
logging.info("Edge 浏览器驱动初始化成功(使用本地驱动)")
return driver
except Exception as e:
logging.error(f"浏览器驱动初始化失败:{e}")
return None登录服务(src/services/login.py)
实现校园网登录的核心逻辑:
import logging
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from config.loader_config import (
CAMPUS_LOGIN_URL, EXTRANET_TEST_DOMAIN, AUTH_ID,
ACCOUNT_BTN_XPATH, USERNAME_ID, PASSWORD_ID,
LOGIN_BTN_XPATH, ONLINE_FLAG, USERNAME, PASSWORD
)
from utils.network import ping_test
def login_campus_net(driver):
"""
校园网登录核心逻辑
"""
try:
# 先检查是否已登录,避免重复操作
if ping_test(EXTRANET_TEST_DOMAIN):
logging.info("已登录校园网,无需登录")
return True
logging.info("开始自动登录...")
driver.get(CAMPUS_LOGIN_URL)
# 1. 点击统一身份认证按钮
WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.ID, AUTH_ID))).click()
# 2. 切换到账号登录模式
WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.XPATH, ACCOUNT_BTN_XPATH))).click()
# 3. 输入账号
WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.ID, USERNAME_ID))).send_keys(USERNAME)
# 4. 输入密码
WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.ID, PASSWORD_ID))).send_keys(PASSWORD)
# 5. 点击登录按钮
login_button = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, LOGIN_BTN_XPATH)))
login_button.click()
# 6. 验证登录成功
WebDriverWait(driver, 10).until(
EC.text_to_be_present_in_element((By.CSS_SELECTOR, 'div[name="PageTips"]'), ONLINE_FLAG)
)
logging.info("登录成功!")
return True
except Exception as e:
logging.error(f"登录失败:{e}")
return False重试机制(src/services/retry.py)
处理登录失败和会话失效的重试逻辑:
import time
import logging
from selenium.common import InvalidSessionIdException
from utils.driver import init_edge_driver
from config.loader_config import RETRY_LIMIT
from services.login import login_campus_net
def retry_login(driver, attempts=0):
"""
登录失败重试机制,支持会话失效时自动重建驱动
"""
while attempts < RETRY_LIMIT:
try:
# 检查会话状态
current_title = driver.title
logging.info(f"第{attempts + 1}次尝试,当前会话状态正常(页面标题:{current_title})")
if login_campus_net(driver):
return True
logging.warning(f"登录失败,正在重试... (尝试 {attempts + 1}/{RETRY_LIMIT})")
attempts += 1
time.sleep(30)
except InvalidSessionIdException:
# 会话失效,重建浏览器实例
logging.info(f"第{attempts + 1}次尝试发现会话失效,正在重建浏览器实例...")
driver.quit()
driver = init_edge_driver()
attempts += 1
time.sleep(1)
logging.error("达到最大重试次数,登录失败!")
return False三、使用教程
1. 安装必要依赖
打开命令行,进入项目根目录,执行以下命令安装依赖:
pip install -r requirements.txt如果需要手动安装,可以使用:
pip install selenium2. 配置文件设置
- 打开
resource/config.json文件 - 修改以下关键配置项:
username: 你的校园网账号password: 你的校园网密码campus_login_url: 校园网登录页URLdriver_path: EdgeDriver的绝对路径- 其他配置项根据需要调整
3. 运行程序
直接运行Python脚本
cd src
python main.py打包成可执行文件
安装打包工具:
pip install pyinstaller执行打包命令:
cd src pyinstaller -F -n "CampusNetworkAutoLogin" main.py打包后文件位置:
- 生成的
CampusNetworkAutoLogin.exe在src/dist/文件夹中
- 生成的
运行准备:
将生成的exe文件与resource文件夹放在同一目录下,确保结构如下:运行目录/ ├─ CampusNetworkAutoLogin.exe # 打包后的程序 └─ resource/ └─ config.json # 配置文件
4. 日志系统
程序会自动创建 logs 文件夹,并按日期生成日志文件,记录网络状态、登录结果等信息,方便排查问题。
5. 常见问题排查
- 配置文件不存在:确保
resource文件夹与程序在同一目录 - 驱动初始化失败:检查
driver_path配置是否正确,确保EdgeDriver版本与本地Edge浏览器匹配 - 登录失败:检查账号密码是否正确,以及页面元素ID/XPath是否与校园网登录页匹配