自动连接校园网wifi脚本实践(自动网页认证)
抽了一下空将自己编写的一个自动化校园网连接脚本的编写思路整理了一下。该脚本只测试过fosu的校园网,但其他校园网的连接方式应该也类似。
起因 经常从实验室、教室和宿舍来回穿梭,每次都要手动连接校园网,非常麻烦。因此,我决定编写一个脚本,自动连接校园网。
思路 首先要对校园网的连接方式进行分析,fosu的校园网是通过网页认证的方式连接的,因此,我选择了使用python的requests库来模拟网页认证。
分析校园网登录逻辑
该过程中需要使用到浏览器的开发者工具,博主使用的是edge浏览器,因此使用了edge的F12功能(快捷键Ctrl+Shift+I)来进行分析。但博主先前给开发者工具设置了中文,因此在下面的图片中出现的文字都是中文,会在博文中用()来标注对应的英文。
登录校园网认证网页,先点击F12,选中网络(Network),并勾选保存日志(Preserve log)。
再在网页输入账户密码,点击login,看看发了哪些请求(一般network的前几个比较重要,后面的都是资源文件)
这里发现发了两个请求,第一个post请求,第二个get请求(每个学校的不一样,fosu的可以直接用本教程方法) 第一个请求post,标头(Headers)中有:
查看一下负载(Payload),查看post请求携带的数据,发现里面包含了自己的校园网账户和密码
第二个请求get:
复制上面的request URL到浏览器,发现就是可以访问的认证界面。
总结:点击login按钮后,我的密码是先通过一个post请求提交到服务器,然后再通过一个get请求去实现登录的。
那脚本要做的事情就是:当发现当前处于未登录状态时,要模拟浏览器的行为,先发一个post请求,请求header和data都要和浏览器上的内容对应,然后再发一个get请求就可以了。(有点像爬虫做的事情)
判断当前是否处于未登录状态 一般提交get请求时,当前已经登录和为登录状态得到html文档中的标签内容是不一样的。这里我们在已登录状态下,按F12,再按ctrl+F查找下的内容,发现果然有标识。
也就是说,当get请求结果返回的title标签内容是“登录成功”时,当前就已经处于登录状态了,此时不需要再进行认证,如果不是tittle标签内容不是“登录成功”,则说明当前未登录,需要发请求进行认证。
代码设计 首先梳理一下脚本的大致功能逻辑,然后后面再对代码进行功能扩充、优化修改。
粗设计
写一个死循环,不断判断当前是否处于登录状态:直接发get请求,如果返回的tittle为“登录成功”,则为已登录状态。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 while (True ): print ("自动联网脚本开始运行..." ) try : response = request.urlopen(get_URL) html = response.read() res = re.findall('<title>(.*)</title>' , html.decode(encoding="GBK" , errors="strict" )) print ('res:' , res) title = '' if len (res) == 0 : print ("访问" ,get_URL,"失败,请检查请求地址!" ) pass else : title = res[0 ] print ("title:" ,title)
否则的话,就模拟浏览器的行为,给服务器发一个post请求(设置好header和data,示例如下),然后再发一个get请求进行认证。
2.1 设置header
将请求标头(Request Headers)中的内容复制到代码中,注意header中的内容是字典类型的,因此要将其转换为字典类型。示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 header = { "Accept" : "*/*" , "Accept-Encoding" : "gzip, deflate" , "Accept-Language" : "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6" , "Connection" : "keep-alive" , "Content-Length" : "781" , "Content-Type" : "application/x-www-form-urlencoded; charset=UTF-8" , "Cookie" : "EPORTAL_COOKIE_DOMAIN=false; EPORTAL_COOKIE_SAVEPASSWORD=true; EPORTAL_COOKIE_OPERATORPWD=; EPORTAL_COOKIE_NEWV=true; EPORTAL_COOKIE_PASSWORD=0c8e03e937910c800890ec544b109fce824381097eba5b4e91011f2c3615a959563377abf814d21320f9f0c98a6615cf42b17d3920473acdb51447afc135c59e2e8a69b4136afde0b623a64e56fc0a82f330f70ce253235e999cc858bff65867a22053c8186fd148a20e0ba3d45ced58808990a864a4f39c72f368ddf0086582; EPORTAL_AUTO_LAND=; EPORTAL_COOKIE_SERVER=%E7%A7%BB%E5%8A%A8; EPORTAL_COOKIE_SERVER_NAME=%E7%A7%BB%E5%8A%A8; EPORTAL_USER_GROUP=Student; EPORTAL_COOKIE_USERNAME=; JSESSIONID=BD9937825302ECA25A22692E95D92CF1" , "Host" : "10.10.9.4" , "Origin" : "http://10.10.9.4" , "Referer" : "http://10.10.9.4/eportal/index.jsp?wlanuserip=10.201.220.90&wlanacname=FSN-XX-Business&ssid=&nasip=10.10.9.1&snmpagentip=&mac=44fa66c84741&t=wireless-v2-plain&url=http://www.msftconnecttest.com/redirect&apmac=&nasid=FSN-XX-Business&vid=3326&port=472&nasportid=AggregatePort%20175.33260000:3326-0" , "User-Agent" : "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36 Edg/131.0.0.0" , }
将请求负载(Payload)中的内容复制到代码中,注意data中的内容是字典类型的,因此要将其转换为字典类型。示例如下:
1 2 3 4 5 6 7 8 9 10 data = { "userId" : 'XXXX' , "password" : '0c937910c800890ec544b109fce824381097eba5b4e91011f2c3615a9595abf814d21320f9f0c98a642b17d3920473acdb51447afc135c59e2e9b413e0b623a64e56fc0a82f330f70ce253235e999cc85a22053c8186fd148a20e0ba3d45ced58808990a864a4f39c72f3f0086582' , "queryString" : 'wlanuserip%3D10.201.220.90%26wlanacname%3DFSN-XX-Business%26ssid%3D%26nasip%3D10.10.9.1%26snmpagentip%3D%26mac%3D44fa66c84741%26t%3Dwireless-v2-plain%26url%3Dhttp%3A%2F%2Fwww.msftconnecttest.com%2Fredirect%26apmac%3D%26nasid%3DFSN-XX-Business%26vid%3D3326%26port%3D472%26nasportid%3DAggregatePort%2520175.33260000%3A3326-0' , "passwordEncrypt" : 'true' , "operatorPwd" : '' , "operatorUserId" : '' , "validcode" : '' , "service" : '%E7%A7%BB%E5%8A%A8' , }
发post请求和get请求,并打印状态码(状态码为200则为成功)
1 2 3 4 5 6 7 8 9 10 post_response = requests.post(post_URL, data, headers=header) print (f"post请求状态码 {post_response.status_code} " ) uft_str = post_response.text.encode("iso-8859-1" ).decode('utf-8' ) schoolWebLoginURL = get_URL get_status = requests.get(schoolWebLoginURL).status_code print (f"get请求状态码 {get_status} " )
1 2 3 4 rand = random.uniform(0 , 100 ) print ("休眠" ,int (3600.0 + rand),"s" )time.sleep(3600.0 + rand)
这样我们就有了一个初始功能的脚本代码了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 import re from urllib import requestimport requestsimport timeimport randomimport oslog_file_path = 'D:/win11/desktop/auto_connect.log' post_URL = 'http://10.10.9.4/eportal/InterFace.do?method=login' get_URL = 'http://10.10.9.4/eportal/success.jsp?userIndex=30313761383634643038313231376666613631393530663863623836663665345f31302e3230312e3232302e39305f3230323430333930313432&keepaliveInterval=0' while (True ): print ("自动联网脚本开始运行..." ) try : response = request.urlopen(get_URL) html = response.read() res = re.findall('<title>(.*)</title>' , html.decode(encoding="GBK" , errors="strict" )) print ('res:' , res) title = '' if len (res) == 0 : print ("访问" ,get_URL,"失败,请检查请求地址!" ) pass else : title = res[0 ] print ("title:" ,title) if title == '登录成功' : print ('当前状态为:已登陆成功!' ) else : print ('当前状态为:未登录!' ) header = { "Accept" : "*/*" , "Accept-Encoding" : "gzip, deflate" , "Accept-Language" : "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6" , "Connection" : "keep-alive" , "Content-Length" : "781" , "Content-Type" : "application/x-www-form-urlencoded; charset=UTF-8" , "Cookie" : "EPORTAL_COOKIE_DOMAIN=false; EPORTAL_COOKIE_SAVEPASSWORD=true; EPORTAL_COOKIE_OPERATORPWD=; EPORTAL_COOKIE_NEWV=true; EPORTAL_COOKIE_PASSWORD=0c8e03e937910c800890ec544b109fce824381097eba5b4e91011f2c3615a959563377abf814d21320f9f0c98a6615cf42b17d3920473acdb51447afc135c59e2e8a69b4136afde0b623a64e56fc0a82f330f70ce253235e999cc858bff65867a22053c8186fd148a20e0ba3d45ced58808990a864a4f39c72f368ddf0086582; EPORTAL_AUTO_LAND=; EPORTAL_COOKIE_SERVER=%E7%A7%BB%E5%8A%A8; EPORTAL_COOKIE_SERVER_NAME=%E7%A7%BB%E5%8A%A8; EPORTAL_USER_GROUP=Student; EPORTAL_COOKIE_USERNAME=; JSESSIONID=BD9937825302ECA25A22692E95D92CF1" , "Host" : "10.10.9.4" , "Origin" : "http://10.10.9.4" , "Referer" : "http://10.10.9.4/eportal/index.jsp?wlanuserip=10.201.220.90&wlanacname=FSN-XX-Business&ssid=&nasip=10.10.9.1&snmpagentip=&mac=44fa66c84741&t=wireless-v2-plain&url=http://www.msftconnecttest.com/redirect&apmac=&nasid=FSN-XX-Business&vid=3326&port=472&nasportid=AggregatePort%20175.33260000:3326-0" , "User-Agent" : "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36 Edg/131.0.0.0" , } data = { "userId" : 'XXXX' , "password" : '0c937910c800890ec544b109fce824381097eba5b4e91011f2c3615a9595abf814d21320f9f0c98a642b17d3920473acdb51447afc135c59e2e9b413e0b623a64e56fc0a82f330f70ce253235e999cc85a22053c8186fd148a20e0ba3d45ced58808990a864a4f39c72f3f0086582' , "queryString" : 'wlanuserip%3D10.201.220.90%26wlanacname%3DFSN-XX-Business%26ssid%3D%26nasip%3D10.10.9.1%26snmpagentip%3D%26mac%3D44fa66c84741%26t%3Dwireless-v2-plain%26url%3Dhttp%3A%2F%2Fwww.msftconnecttest.com%2Fredirect%26apmac%3D%26nasid%3DFSN-XX-Business%26vid%3D3326%26port%3D472%26nasportid%3DAggregatePort%2520175.33260000%3A3326-0' , "passwordEncrypt" : 'true' , "operatorPwd" : '' , "operatorUserId" : '' , "validcode" : '' , "service" : '%E7%A7%BB%E5%8A%A8' , } post_response = requests.post(post_URL, data, headers=header) print (f"post请求状态码 {post_response.status_code} " ) uft_str = post_response.text.encode("iso-8859-1" ).decode('utf-8' ) schoolWebLoginURL = get_URL get_status = requests.get(schoolWebLoginURL).status_code print (f"get请求状态码 {get_status} " ) except Exception as e: print (f"发生错误: {str (e)} " ) error_msg = str (e) with open (log_file_path, 'a' , encoding='utf-8' ) as log_file: log_file.write(f"[{time.ctime()} ] 自动联网脚本开始运行\n" ) log_file.write(f"当前状态: {'已登陆成功' if title == '登录成功' else '未登录' } \n" ) if 'post_response' in locals (): log_file.write(f"POST状态码: {post_response.status_code} \n" ) if 'get_status' in locals (): log_file.write(f"GET状态码: {get_status} \n" ) if 'error_msg' in locals (): log_file.write(f"错误信息: {error_msg} \n" ) log_file.write("-" *50 + "\n" ) if os.path.getsize(log_file_path) > 1024 : open (log_file_path, 'w' ).close() rand = random.uniform(0 , 100 ) print ("休眠" ,int (3600.0 + rand),"s" ) time.sleep(3600.0 + rand)
优化
检测设备的网络连接状态。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 def check_network_connection (): """检查网络连接状态""" targets = [ "http://connectivitycheck.gstatic.com/generate_204" , "http://www.msftconnecttest.com/connecttest.txt" , "http://connect.rom.miui.com/generate_204" ] timeout = 8 for url in targets: try : with urllib.request.urlopen(url, timeout=timeout) as response: if response.status == 204 : return True except : continue return False
根据网络连接状态来决定是否进行自动化连接。1 2 3 4 while True : while not check_network_connection(): auto_login(post_URL, get_URL, log_file_path) print ("网络已恢复,自动联网脚本结束运行" )
再将之前的休眠改动位置,更改时间为1min左右。1 2 3 4 5 6 7 8 9 while True : while not check_network_connection(): auto_login(post_URL, get_URL, log_file_path) print ("网络已恢复,自动联网脚本结束运行" ) rand = random.uniform(0 , 20 ) print ("休眠" ,int (60.0 + rand),"s" ) time.sleep(60.0 + rand)
最终代码
注:有关参数的设置需要按照上面的步骤结合自己实际的情况进行修改,还不支持直接下载源码使用!!!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 import re from urllib import requestimport urllib.requestimport requestsimport timeimport randomimport osdef check_network_connection (): """检查网络连接状态""" targets = [ "http://connectivitycheck.gstatic.com/generate_204" , "http://www.msftconnecttest.com/connecttest.txt" , "http://connect.rom.miui.com/generate_204" ] timeout = 8 for url in targets: try : with urllib.request.urlopen(url, timeout=timeout) as response: if response.status == 204 : return True except : continue return False def auto_login (post_URL, get_URL, log_file_path ): print ("自动联网脚本开始运行..." ) try : response = request.urlopen(get_URL) html = response.read() res = re.findall('<title>(.*)</title>' , html.decode(encoding="GBK" , errors="strict" )) print ('res:' , res) title = '' if len (res) == 0 : print ("访问" ,get_URL,"失败,请检查请求地址!" ) pass else : title = res[0 ] print ("title:" ,title) if title == '登录成功' : print ('当前状态为:已登陆成功!' ) else : print ('当前状态为:未登录!' ) header = { "Accept" : "*/*" , "Accept-Encoding" : "gzip, deflate" , "Accept-Language" : "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6" , "Connection" : "keep-alive" , "Content-Length" : "781" , "Content-Type" : "application/x-www-form-urlencoded; charset=UTF-8" , "Cookie" : "EPORTAL_COOKIE_DOMAIN=false; EPORTAL_COOKIE_SAVEPASSWORD=true; EPORTAL_COOKIE_OPERATORPWD=; EPORTAL_COOKIE_NEWV=true; EPORTAL_COOKIE_PASSWORD=0c8e03e937910c800890ec544b109fce824381097eba5b4e91011f2c3615a959563377abf814d21320f9f0c98a6615cf42b17d3920473acdb51447afc135c59e2e8a69b4136afde0b623a64e56fc0a82f330f70ce253235e999cc858bff65867a22053c8186fd148a20e0ba3d45ced58808990a864a4f39c72f368ddf0086582; EPORTAL_AUTO_LAND=; EPORTAL_COOKIE_SERVER=%E7%A7%BB%E5%8A%A8; EPORTAL_COOKIE_SERVER_NAME=%E7%A7%BB%E5%8A%A8; EPORTAL_USER_GROUP=Student; EPORTAL_COOKIE_USERNAME=; JSESSIONID=BD9937825302ECA25A22692E95D92CF1" , "Host" : "10.10.9.4" , "Origin" : "http://10.10.9.4" , "Referer" : "http://10.10.9.4/eportal/index.jsp?wlanuserip=10.201.220.90&wlanacname=FSN-XX-Business&ssid=&nasip=10.10.9.1&snmpagentip=&mac=44fa66c84741&t=wireless-v2-plain&url=http://www.msftconnecttest.com/redirect&apmac=&nasid=FSN-XX-Business&vid=3326&port=472&nasportid=AggregatePort%20175.33260000:3326-0" , "User-Agent" : "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36 Edg/131.0.0.0" , } data = { "userId" : 'XXXX' , "password" : '0c937910c800890ec544b109fce824381097eba5b4e91011f2c3615a9595abf814d21320f9f0c98a642b17d3920473acdb51447afc135c59e2e9b413e0b623a64e56fc0a82f330f70ce253235e999cc85a22053c8186fd148a20e0ba3d45ced58808990a864a4f39c72f3f0086582' , "queryString" : 'wlanuserip%3D10.201.220.90%26wlanacname%3DFSN-XX-Business%26ssid%3D%26nasip%3D10.10.9.1%26snmpagentip%3D%26mac%3D44fa66c84741%26t%3Dwireless-v2-plain%26url%3Dhttp%3A%2F%2Fwww.msftconnecttest.com%2Fredirect%26apmac%3D%26nasid%3DFSN-XX-Business%26vid%3D3326%26port%3D472%26nasportid%3DAggregatePort%2520175.33260000%3A3326-0' , "passwordEncrypt" : 'true' , "operatorPwd" : '' , "operatorUserId" : '' , "validcode" : '' , "service" : '%E7%A7%BB%E5%8A%A8' , } post_response = requests.post(post_URL, data, headers=header) print (f"post请求状态码 {post_response.status_code} " ) uft_str = post_response.text.encode("iso-8859-1" ).decode('utf-8' ) schoolWebLoginURL = get_URL get_status = requests.get(schoolWebLoginURL).status_code print (f"get请求状态码 {get_status} " ) except Exception as e: print (f"发生错误: {str (e)} " ) error_msg = str (e) with open (log_file_path, 'a' , encoding='utf-8' ) as log_file: log_file.write(f"[{time.ctime()} ] 自动联网脚本开始运行\n" ) log_file.write(f"当前状态: {'已登陆成功' if title == '登录成功' else '未登录' } \n" ) if 'post_response' in locals (): log_file.write(f"POST状态码: {post_response.status_code} \n" ) if 'get_status' in locals (): log_file.write(f"GET状态码: {get_status} \n" ) if 'error_msg' in locals (): log_file.write(f"错误信息: {error_msg} \n" ) log_file.write("-" *50 + "\n" ) if os.path.getsize(log_file_path) > 1024 : open (log_file_path, 'w' ).close() if __name__ == "__main__" : log_file_path = 'auto_connect.log' if not os.path.exists(log_file_path): open (log_file_path, 'w' ).close() post_URL = 'http://10.10.9.4/eportal/InterFace.do?method=login' get_URL = 'http://10.10.9.4/eportal/success.jsp?userIndex=30313761383634643038313231376666613631393530663863623836663665345f31302e3230312e3232302e39305f3230323430333930313432&keepaliveInterval=0' while True : while not check_network_connection(): auto_login(post_URL, get_URL, log_file_path) print ("网络已恢复,自动联网脚本结束运行" ) rand = random.uniform(0 , 20 ) print ("休眠" ,int (60.0 + rand),"s" ) time.sleep(60.0 + rand)
注意:本脚本只是用于自动做校园网账户的认证(在已经连着校园网的情况下才能验证),并不是让windows去自动连接某个校园网wifi。所以在平时使用时建议把校园网wifi的“自动连接”打开。或者尝试自己设计自动连接校园网wifi的脚本。(博主尝试过,但自动连接wifi时总会在桌面弹出校园网的浏览器登录网页,博主嫌功能一般、累赘就没添加)
脚本需要安装依赖的模块和库,出现警告时检查是否缺少依赖模块。
开机自启动
提供两种方式,一种是将编写好的Python源码编译成可执行文件exe,另一种是使用bat批处理文件来执行Python脚本(这种便于开发调试,实时修改)。
编译成可执行文件exe
安装pyinstaller模块
编译成可执行文件
1 pyinstaller -F AutoConnect.py
编译好的exe文件在dist目录下
win+r,输入shell:startup
把.exe拖入开机自启动文件夹里面
bat批处理脚本
编写bat批处理文件 新建一个bat文件,内容如下:1 2 3 @echo off python "这里填写你的Python脚本路径" pause
win+r,输入shell:startup
把.bat拖入开机自启动文件夹里面
最终成品效果
脚本已推送到github:Auto_Connect