Python實(shí)現(xiàn)全自動(dòng)校園網(wǎng)認(rèn)證與登錄腳本
一、項(xiàng)目背景
在現(xiàn)代大學(xué)生活中,穩(wěn)定且高效的網(wǎng)絡(luò)連接是學(xué)生日常學(xué)習(xí)和生活的重要組成部分。然而,許多學(xué)校的校園網(wǎng)需要通過(guò)網(wǎng)頁(yè)認(rèn)證登錄,這不僅繁瑣而且耗時(shí)。為了簡(jiǎn)化這一過(guò)程,我開(kāi)發(fā)了一個(gè)自動(dòng)化腳本,旨在幫助學(xué)生自動(dòng)連接到校園網(wǎng),無(wú)需手動(dòng)操作。
校園網(wǎng)使用痛點(diǎn)
- 設(shè)備重啟后需要重新認(rèn)證
- 網(wǎng)絡(luò)波動(dòng)導(dǎo)致頻繁掉線
- 實(shí)驗(yàn)室設(shè)備需長(zhǎng)期保持在線
傳統(tǒng)解決方案的不足
- 瀏覽器保存密碼存在安全風(fēng)險(xiǎn)
- 第三方工具可能攜帶廣告/后門(mén)
- 無(wú)法自定義檢測(cè)頻率和策略
二、腳本核心功能
| 功能模塊 | 實(shí)現(xiàn)方式 | 技術(shù)亮點(diǎn) |
|---|---|---|
| 網(wǎng)絡(luò)狀態(tài)檢測(cè) | 定時(shí)請(qǐng)求認(rèn)證頁(yè)面+標(biāo)題解析 | 雙編碼兼容(GBK/UTF-8) |
| 自動(dòng)認(rèn)證系統(tǒng) | 模擬瀏覽器POST請(qǐng)求 | 請(qǐng)求頭指紋偽裝 |
| 桌面通知提醒 | win10toast庫(kù) | 非阻塞式彈窗 |
| 運(yùn)行日志系統(tǒng) | 文件寫(xiě)入+自動(dòng)清理 | 日志輪轉(zhuǎn)機(jī)制 |
| 智能休眠策略 | 隨機(jī)間隔檢測(cè) | 防模式識(shí)別 |
三、校園網(wǎng)認(rèn)證邏輯分析
1.先登錄校園網(wǎng)認(rèn)證網(wǎng)頁(yè),點(diǎn)擊F12,選中網(wǎng)絡(luò)并勾選保存日志

2.再輸入賬戶密碼,點(diǎn)擊連接login,進(jìn)行抓包(一般netword的前幾個(gè)比較重要,后面的都是資源文件)

3.這里發(fā)現(xiàn)發(fā)了兩個(gè)請(qǐng)求,第一個(gè)post請(qǐng)求,第二個(gè)get請(qǐng)求(每個(gè)學(xué)校的不一樣,HHU的可以直接用本教程方法)
第一個(gè)是post請(qǐng)求

點(diǎn)擊payload查看post請(qǐng)求攜帶的數(shù)據(jù),發(fā)現(xiàn)里面包含了自己的校園網(wǎng)賬戶和密碼

第二個(gè)是get請(qǐng)求,復(fù)制上面的request URL到瀏覽器,發(fā)現(xiàn)就是可以訪問(wèn)的認(rèn)證界面。

小結(jié):點(diǎn)擊login按鈕后,密碼是先通過(guò)一個(gè)post請(qǐng)求提交到服務(wù)器,然后再通過(guò)一個(gè)get請(qǐng)求去實(shí)現(xiàn)登錄的。
那腳本要做的事情就是:當(dāng)發(fā)現(xiàn)當(dāng)前處于未登錄狀態(tài)時(shí),要模擬瀏覽器的行為,先發(fā)一個(gè)post請(qǐng)求,請(qǐng)求header和data都要和瀏覽器上的內(nèi)容對(duì)應(yīng),然后再發(fā)一個(gè)get請(qǐng)求就可以了。
如何判斷當(dāng)前是否處于未登錄狀態(tài)?
一般提交get請(qǐng)求時(shí),當(dāng)前已經(jīng)登錄和為登錄狀態(tài)得到html文檔中的標(biāo)簽內(nèi)容是不一樣的。這里我們?cè)谝训卿洜顟B(tài)下,按F12,再按ctrl+F查找下的內(nèi)容,發(fā)現(xiàn)果然有標(biāo)識(shí)

四、代碼解析(關(guān)鍵部分)
核心模塊
HTTP請(qǐng)求:使用requests庫(kù)發(fā)送HTTP請(qǐng)求,與校園網(wǎng)認(rèn)證服務(wù)器進(jìn)行通信。
HTML解析:使用re模塊解析HTML內(nèi)容,提取標(biāo)題元素以判斷登錄狀態(tài)。
Windows通知:使用win10toast庫(kù)顯示桌面通知。
日志記錄:將每次操作的結(jié)果記錄到日志文件中。
異常處理:捕獲并處理各種可能的異常情況,確保腳本的穩(wěn)定性。
代碼結(jié)構(gòu)
1.寫(xiě)一個(gè)死循環(huán),不斷判斷當(dāng)前是否處于登錄狀態(tài):直接發(fā)get請(qǐng)求,如果返回的tittle為“登錄成功”,則為已登錄狀態(tài)。
while(True):
print("自動(dòng)聯(lián)網(wǎng)腳本開(kāi)始運(yùn)行...")
# 請(qǐng)求校園網(wǎng)url
response = request.urlopen(get_URL)
html = response.read()
# 獲取tittle元素內(nèi)容
res = re.findall('<title>(.*)</title>', html.decode(encoding="GBK", errors="strict"))
print('res:', res)
title = ''
if len(res) == 0:
print("訪問(wèn)",get_URL,"失敗,請(qǐng)檢查請(qǐng)求地址!")
pass
else:
title = res[0]
print("title:",title)
2.否則的話,就模擬瀏覽器的行為,給服務(wù)器發(fā)一個(gè)post請(qǐng)求(設(shè)置好header和data,示例如下),然后再發(fā)一個(gè)get請(qǐng)求進(jìn)行認(rèn)證。
設(shè)置header操作如下:查看請(qǐng)求一Headers,鼠標(biāo)向下滑,找到Request Header,將自己的配置復(fù)制到代碼相對(duì)應(yīng)的配置中。

# 設(shè)置post的請(qǐng)求頭,瀏覽器點(diǎn)擊F12,在Netword中選中post請(qǐng)求,點(diǎn)擊Headers、request header面板中查看
header = {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7",
"Connection": "keep-alive",
"Content-Length": "762",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Cookie": "EPORTAL_COOKIE_DOMAIN=false; EPORTAL_COOKIE_USERNAME=XXXXX; EPORTAL_COOKIE_SERVER=%E4%B8%AD%E5%9%E7%A7%BB%E5%8A%A8(CMCC%20NET); EPORTAL_COOKIE_SERVER_NAME=%E4%B8%AD%E5%9B%BA7%BB%E5%8A%A8(CMCC%20NET); EPORTAL_COOKIE_SAVEPASSWORD=true; EPORTAL_COOKIE_OPERATORPWD=; EPORTAL_COOKIE_NEWV=true; EPORTAL_COOKIE_PASSWORD=1e485d5861f50092df261f37ca6218c4d8675e6daf226f84489f5bd7ca8339a4e15b27b2fdb3a1ade55b553c96a04a76ad00a31cb46902d356babec2ced138dc40f97b6f5b489274aa5561d24a6f9610caf99e52e5a0d92bf2448819f44dfc2f2c37966d8554aa00fe530d0cbe52a0d4438f2640f04410e865ff3aeff6faf9ff; EPORTAL_AUTO_LAND=; EPORTAL_USER_GROUP=%E5%AD%A6%E7%94%9F; JSESSIONID=8A3372E32254B5F7321DF7B93A4851AA; JSESSIONID=F2C1BB4E6D58762763F36630541B5C38",
"Host": "eportal.hhu.edu.cn",
"Origin": "http://eportal.hhu.edu.cn",
"Referer": "http://eportal.hhu.edu.cn/eportal/index.jsp?wlanuserip=29fc0b608918b04682c9e6c6cf6c1c29&wlanacname=2356e8aa38c836625d91257381aaef57&ssid=ea65e712d7d12a1fb44ec48a1c5072b0&nasip=07ec241dffc0de15d87efe9c07b8c6e0&mac=027a460789bb1e7c9c4acc766c937e6e&t=wireless-v2&url=35e6780db7fde27a90f8986393791ca7b01578112b560bd2",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
}
設(shè)置data

# 設(shè)置post的請(qǐng)求數(shù)據(jù),瀏覽器點(diǎn)擊F12,在Netword中選中post請(qǐng)求,點(diǎn)擊payload面板中查看
data = {
"userId": 'XXX', # 需要根據(jù)自己的情況修改
"password": '1e485d5861f50092df261f37ca621daf226f84489f5bd5b27b2fdb3a1ade55b553c96a04a76ad00aa31cb4ced138dc40f97b6f5b489274a6f9610caf99e52e5a0d92bf2448819f44dfc2f2c37966d8554ada00fe530d0cbe52a0d4438f2640f04410e865ff3aeff6faf9ff', # 需要根據(jù)自己的情況修改
"queryString": 'wlanuserip%3D29fc0b608918b04682c9e6c6cf6c1c29%26wlanacname%3D2356e8aa38c836625d91257381aaef57%26ssid%3Dea65e712d7d12a1fb44ec48a1c5072b0%26nasip%3D07ec241dffc0de15d87efe9c07b8c6e0%26mac%3D027a460789bb1e7c9c4acc766c937e6e%26t%3Dwireless-v2%26url%3D35e6780db7fde27a90f8986393791ca7b01578112b560bd2',
"passwordEncrypt": 'true',
"operatorPwd": '',
"operatorUserId": '',
"validcode": '',
"service": '',
}
3.打印狀態(tài)碼,判斷是否認(rèn)證成功。
4.休眠一段時(shí)間,然后進(jìn)行下一次循環(huán)。
# 每1h左右檢測(cè)一次是否成功連接
rand = random.uniform(0, 100)
print("休眠",int(3600.0 + rand),"s")
time.sleep(3600.0 + rand)
另外,由于這個(gè)腳本可以放到需要后臺(tái)運(yùn)行,把日志輸出到log文件中去,可以查看腳本運(yùn)行狀態(tài)。
以下是腳本的主要部分:
import urllib.request
from urllib import parse
import time
import random
import os
import re
# 設(shè)置日志文件路徑
log_file_path = 'log.txt'
# 第一個(gè)post請(qǐng)求的URL
post_URL = 'http://10.100.255.2/eportal/InterFace.do?method=login'
# 第二個(gè)get請(qǐng)求的URL(瀏覽器可訪問(wèn)的url)
get_URL = 'http://10.100.255.2/eportal/success.jsp?userIndex=63360363366306533343048342464646&keepaliveInterval=0'
while True:
print("自動(dòng)聯(lián)網(wǎng)腳本運(yùn)行中...")
try:
# 請(qǐng)求校園網(wǎng)url(添加超時(shí)防止阻塞)
response = urllib.request.urlopen(get_URL, timeout=10)
html = response.read()
except Exception as e:
print(f"網(wǎng)絡(luò)請(qǐng)求異常: {e}")
html = b'' # 防止html未定義
# 獲取title元素內(nèi)容
res = re.findall('<title>(.*)</title>', html.decode(encoding="GBK", errors="ignore")) # 使用errors="ignore"避免解碼失敗
print('res:', res)
title = ''
if len(res) == 0:
print(f"訪問(wèn) {get_URL} 可能未連接到校園網(wǎng)!")
else:
title = res[res[0]]
# 根據(jù)title判斷登錄狀態(tài)
if title == '登錄成功':
print('當(dāng)前狀態(tài)為:已登錄!')
# 彈出Windows通知
# try:
# toaster.show_notification(
# "校園網(wǎng)狀態(tài)",
# "✅ 已成功連接到校園網(wǎng)!",
# duration=5,
# icon_path="school.ico", # 可替換為圖標(biāo)路徑如 "school.ico"
# threaded=True
# )
# except Exception as e:
# print(f"通知發(fā)送失敗: {e}")
else:
print('當(dāng)前狀態(tài)為:未登錄!')
# 設(shè)置post請(qǐng)求頭和數(shù)據(jù)
header = {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,zh;q=0.9",
"Connection": "keep-alive",
"Content-Length": "691",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Cookie": "EPORTAL_COOKIE_SERVER=; EPORTAL_COOKIE_DOMAIN=; EPORTAL_COOKIE_SAVEPASSWORD=true; EPORTAL_COOKIE_OPERATORPWD=; EPORTAL_COOKIE_USERNAME=SCXY15182972294; EPORTAL_COOKIE_NEWV=true; EPORTAL_COOKIE_SERVER_NAME=;",
"Host": "XXXX", #根據(jù)自己的瀏覽器配置項(xiàng)配置
"Origin": "XXXXX", #根據(jù)自己的瀏覽器配置項(xiàng)配置
"Referer": "XXXXX", #根據(jù)自己的瀏覽器配置項(xiàng)配置
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36",
}
data = {
"userId": 'XXXXX', #根據(jù)自己的瀏覽器配置項(xiàng)配置
"password": 'XXXXX', #根據(jù)自己的瀏覽器配置項(xiàng)配置
"queryString": 'XXXXX', #根據(jù)自己的瀏覽器配置項(xiàng)配置
"passwordEncrypt": 'true',
"operatorPwd": '',
"operatorUserId": '',
"validcode": '',
"service": '',
}
# 發(fā)送登錄請(qǐng)求
try:
post_response = requests.post(post_URL, data=data, headers=header, timeout=15)
print(f"POST請(qǐng)求狀態(tài)碼: {post_response.status_code}")
get_response = requests.get(get_URL, timeout=15)
print(f"GET請(qǐng)求狀態(tài)碼: {get_response.status_code}")
except requests.exceptions.RequestException as e:
print(f"請(qǐng)求異常: {e}")
# 日志記錄(添加異常處理)
try:
with open(log_file_path, 'a', encoding='utf-8') as log_file:
log_file.write(
f"{time.strftime('%Y-%m-%d %H:%M:%S')} - 狀態(tài): {'已登錄' if title == '登錄成功' else '未登錄'}\n")
if os.path.getsize(log_file_path) > 1024:
log_file.seek(0)
log_file.truncate() # 更安全的清空方式
except IOError as e:
print(f"日志寫(xiě)入失敗: {e}")
# 隨機(jī)休眠(1小時(shí)±100秒)
delay = 3600 + random.uniform(-100, 100)
print(f"下次檢測(cè)將在 {int(delay)} 秒后...\n")
time.sleep(delay)
使用方法
1.安裝依賴:確保已安裝requests和win10toast庫(kù)。可以通過(guò)以下命令安裝:
pip install requests #必須安裝 pip install requests win10toast #如果要使用Windows彈窗功能可以安裝(選擇性安裝)
2.運(yùn)行腳本:直接運(yùn)行Python腳本即可。腳本會(huì)自動(dòng)檢測(cè)當(dāng)前網(wǎng)絡(luò)狀態(tài),并根據(jù)需要進(jìn)行登錄操作。
3.打包:
先安裝好pyinstall工具
pip install pyinstall
最后用pyinstall工具把.py文件打包成.exe可執(zhí)行文件
完整打包命令參考
pyinstaller --onefile --noconsole --hidden-import=win10toast --icon=my_icon.ico your_script.py
--onefile: 打包成單個(gè) .exe 文件。
--noconsole: 禁止控制臺(tái)彈窗。
--hidden-import=win10toast: 顯式包含 win10toast 依賴(如果自動(dòng)檢測(cè)失?。?。
--icon=my_icon.ico: 可選,為 .exe 添加自定義圖標(biāo)。
打包后如下,在dist里面找到.exe可執(zhí)行程序,雙擊測(cè)試運(yùn)行。


4.設(shè)置開(kāi)機(jī)自啟動(dòng),完全解放雙手

鍵盤(pán)同時(shí)按住"win","R"鍵打開(kāi)命令窗口,輸入以下命令:

將剛剛發(fā)送到的快捷方式拖入打開(kāi)的文件夾中即可每次在開(kāi)機(jī)時(shí)自動(dòng)運(yùn)行程序,無(wú)需手動(dòng)啟動(dòng)。

注意:建議將源文件夾放在一個(gè)不長(zhǎng)修改的地方。
五、效果展示
日志文件
2024-03-01 09:00:00 - 狀態(tài): 已登錄
2024-03-01 10:05:23 - 狀態(tài): 未登錄

運(yùn)行截圖

六、擴(kuò)展方向(按需選擇)
1.多平臺(tái)支持
# 適配Linux通知
import notify2
notify2.init("Campus Network")
2.微信通知集成
# 通過(guò)Server醬發(fā)送提醒
requests.post("https://sc.ftqq.com/SCUKEY.send",
data={"text": "網(wǎng)絡(luò)狀態(tài)提醒"})
3.可視化監(jiān)控面板
# 使用Flask搭建Web界面 pip install flask
七、注意事項(xiàng)
1.信息安全
不要明文存儲(chǔ)密碼
建議使用環(huán)境變量保存敏感信息
2.網(wǎng)絡(luò)合規(guī)
僅限個(gè)人設(shè)備使用
遵守校園網(wǎng)使用規(guī)定
3.異常處理
遇到持續(xù)認(rèn)證失敗應(yīng)停止嘗試
添加最大重試次數(shù)限制
九、總結(jié)
這個(gè)自動(dòng)化腳本極大地簡(jiǎn)化了校園網(wǎng)連接的過(guò)程,節(jié)省了學(xué)生的時(shí)間和精力。通過(guò)定期檢測(cè)和自動(dòng)登錄,確保用戶始終能夠保持在線狀態(tài)。同時(shí),日志記錄和錯(cuò)誤處理機(jī)制也提高了腳本的可靠性和易用性。希望這個(gè)腳本能夠幫助更多的學(xué)生享受更加便捷的網(wǎng)絡(luò)體驗(yàn)。
以上就是Python實(shí)現(xiàn)全自動(dòng)校園網(wǎng)認(rèn)證與登錄腳本的詳細(xì)內(nèi)容,更多關(guān)于Python校園網(wǎng)認(rèn)證登錄的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Python實(shí)現(xiàn)求一個(gè)集合所有子集的示例
今天小編就為大家分享一篇Python 實(shí)現(xiàn)求一個(gè)集合所有子集的示例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-05-05
Python面向?qū)ο蟪绦蛟O(shè)計(jì)之類的定義與繼承簡(jiǎn)單示例
這篇文章主要介紹了Python面向?qū)ο蟪绦蛟O(shè)計(jì)之類的定義與繼承,結(jié)合完整實(shí)例形式分析了Python面向?qū)ο蟪绦蛟O(shè)計(jì)中類的定義、調(diào)用、繼承及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2019-03-03
python3如何使用Requests測(cè)試帶簽名的接口
這篇文章主要介紹了python3如何使用Requests測(cè)試帶簽名的接口,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02
django API 中接口的互相調(diào)用實(shí)例
這篇文章主要介紹了django API 中接口的互相調(diào)用實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-04-04
對(duì)python中的six.moves模塊的下載函數(shù)urlretrieve詳解
今天小編就為大家分享一篇對(duì)python中的six.moves模塊的下載函數(shù)urlretrieve詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-12-12
python3使用pyqt5制作一個(gè)超簡(jiǎn)單瀏覽器的實(shí)例
下面小編就為大家?guī)?lái)一篇python3使用pyqt5制作一個(gè)超簡(jiǎn)單瀏覽器的實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-10-10

