用 requests 写一个校园网自动登录脚本
实验室的校园网每 24 小时踢一次线,跑训练任务跑到一半断网是最烦的事。手工点登录太蠢,于是写了个脚本:开机自启,检测到掉线就自己登回去。
先抓包,别急着写代码
打开浏览器开发者工具(F12)→ Network → 勾上 Preserve log,然后手动断网重登一次,找到那个 POST 请求。需要确认三件事:
- 登录接口的完整 URL(常见是
srun_portal/eportal/InterFace.do/login) - 表单里到底提交了哪些字段
- 返回体里"成功"长什么样
最小可用的登录函数
import requests
PORTAL = "http://10.21.0.5/eportal/InterFace.do?method=login"
def login(user: str, pwd: str) -> bool:
s = requests.Session()
s.headers.update({
"User-Agent": "Mozilla/5.0",
"Referer": "http://10.21.0.5/",
})
# 先访问一次认证页,把 cookie 建立起来
s.get("http://10.21.0.5/", timeout=5)
data = {
"userId": user,
"password": pwd,
"service": "",
"queryString": "",
"operatorPwd": "",
"validcode": "",
}
r = s.post(PORTAL, data=data, timeout=8)
return "success" in r.text.lower()
踩过的四个坑
1. 直接 POST 返回 403
因为缺 cookie 和 Referer。Session 只是帮你保持 cookie,不会替你访问首页——要显式先 GET 一次认证页,否则服务端认为你是陌生来源。
2. 密码不是明文传输
抓包时把"你输入的明文"和"实际提交的值"对比一下:如果是一串 32 位十六进制,就是 MD5;如果是带时间戳的一长串 base64,多半是 srun 的 xEncode,需要按学校前端 JS 复刻,或者干脆用 Playwright 驱动真实浏览器。
import hashlib
# 只适用于"摘要"型,具体前缀看抓包结果
password = "{MD5}" + hashlib.md5(raw_password.encode()).hexdigest()
3. "登录成功"的判定方式五花八门
有的返回 JSON {"result":"success"},有的返回 HTML 里含"认证成功",还有的干脆 302 跳转。与其猜返回体,不如直接测外网:
def online() -> bool:
try:
r = requests.get("http://connect.rom.miui.com/generate_204", timeout=3)
return r.status_code == 204
except requests.RequestException:
return False
这个地址在联网时会返回空的 204,断网时请求直接失败,比解析业务返回可靠得多。
4. 脚本跑着跑着就停了
while True: sleep(60) 写在终端里,窗口一关就死。要交给系统托管:Linux 用 systemd,Windows 用任务计划程序并勾选"不管用户是否登录都要运行"。
import time
USER, PWD = "20230001", "your-password"
if __name__ == "__main__":
while True:
if not online():
ok = login(USER, PWD)
print("掉线,重新登录:", ok)
time.sleep(60)
最后提醒一句
别把密码写死在脚本里。用环境变量或 keyring,尤其别把带密码的脚本推上 GitHub——公开仓库里的校园网账号被扫到只是时间问题。