本文介绍了如何利用python爬虫实现对zzuli校园网的自动登录。主要利用requests包,通过模仿浏览器动作对局域网服务器发送请求,实现登录动作。用户不需要等待弹出的上网登录窗、选择运营商等一系列操作,只需要双击程序,即可完成自动登录、挤掉另一个登录账号等功能。本文思路适用于绝大部分高校校园网。
校园网环境(废话)
python3
必要的包:
import os
import socket
import time
import requests
from urllib.parse import parse_qs
from urllib.parse import urlsplit
Chrome浏览器(用来抓包)
pycharm(可有可无)
先说一点题外话,经过我的测试,发现URL中的wlanuserip与wlanacip参数后的IP地址并不是一成不变的,随着时间,学校服务器会改变这个地址。
那么岂不是说无法拿到这个地址喽?拿不到地址怎么能利用python进行后续模拟呢?完全不用担心,细心的话就会发现,Chrome弹开的一瞬间,会有一个地址跳转成上面的那个RequestURL地址。如果用fidder抓包可以抓到,Chrome抓的话手速并没有那么快。所以这里直接给出我抓到的静态地址:
http://www.msftconnecttest.com/redirect
也就是说,上面的地址跳转到当前的这个地址,同时给它了两个重要的参数(这两个参数一直会用到最后)。
总结下,通过对上面的url进行get请求,会重定向到一个带两个重要参数的链接(wlanuserip与wlanacip参数),之所以重要是因为会随着时间变化,如果写死在程序里,只能用一段时间,以后会失效的。【第一个分析结束】
随便提交一个账户密码,然后按照箭头指示的地方清空右侧的请求。
点击登录,选择列表中第一个请求
解释:
下面来看这个动作的请求头和请求数据包
两个参数:
Referer:当浏览器向web发送请求的时候,一般会带上Referer,告诉服务器我是从哪个页面链接过来的,服务器基此可以获得一些信息用于处理。(这个参数由第一个动作构造而成的)
User-Agent:告诉web我是一个浏览器而不是python
这个就是我们的重重之重了。
DDDDD:用户名,0是一个很有魔性的参数,偶尔会变成1,技术太渣、无法深究。10086自然是账号了。unicom自然是联通的意思了。
upass:是密码
另外:我发现R6参数与DDDDD中的0是同步的。所以到时候只能勉强的动态处理下吧。
其他的都是空。
【第二个动作分析完成】
总结:
通过第一个动作对静态链接的访问,拿到两个参数,并通过这两个参数可以构造各种链接、参数。
通过第二个动作对数据的提交,完成登录。
import requests
from urllib.parse import parse_qs
from urllib.parse import urlsplit
# 简单请求头
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36",
}
# 复杂请求头
login_headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
"Referer":"",
}
# url参数表
get_par = {
"wlanuserip":"",#
"wlanacip":"",#
"wlanacname":"",
"vlanid":"0",
"ip":"",#
"ssid":"null",
"areaID":"null",
"mac":"00-00-00-00-00-00"
}
# post url参数表
post_par = {
"hostname":"10.168.6.10",
"iTermType":"1",
"wlanuserip":"10.66.67.119",
"wlanacip":"10.168.6.9",
"mac":"00-00-00-00-00-00",
"ip":"10.66.67.119",
"enAdvert":"0",
"queryACIP":"0",
"loginMethod":"1"
}
static_url = "http://www.msftconnecttest.com/redirect"
# 第一个动作
def get_url():
# 获取静态链接的所有回复内容
static_response = requests.get(static_url, headers=headers)
# 从静态链接302中的跳转页面获取信,用于构造真实的登录页面地址
static_response_302_dict = dict(parse_qs(urlsplit(static_response.url).query))
# 写入请求参数
get_par['wlanuserip'] = static_response_302_dict['wlanuserip'][0]
get_par['wlanacip'] = static_response_302_dict['wlanacip'][0]
get_par['ip'] = static_response_302_dict['wlanuserip'][0]
real_url_head_str = "http://10.168.6.10/a70.htm?"
real_url_par_str = 'wlanuserip=' + str(get_par['wlanuserip']) + \
'&wlanacip=' + str(get_par['wlanacip']) + \
'&wlanacname=' + str(get_par['wlanacname']) + \
'&vlanid=' + str(get_par['vlanid']) + \
'&ip=' + str(get_par['wlanuserip']) + \
'&ssid=' + str(get_par['ssid']) + \
'&areaID=' + str(get_par['areaID']) + \
'&mac=' + str(get_par['mac'])
real_url = real_url_head_str + real_url_par_str
# 构造Referer参数
login_headers["Referer"] = real_url
post_par["wlanuserip"] = get_par["wlanuserip"]
post_par["wlanacip"] = get_par["wlanacip"]
post_par["ip"] = get_par["ip"]
# 最后的提交地址
r_url_head = "http://10.168.6.10:801/eportal/?c=ACSetting&a=Login&protocol=http:"
r_url_par = "&hostname=" + post_par["hostname"] + "&iTermType=" + post_par["iTermType"] + \
"&wlanuserip=" + post_par["wlanuserip"] + "&wlanacip=" + post_par["wlanacip"] + \
"&mac=" + post_par["mac"] + "&ip=" + post_par["ip"] + \
"&enAdvert=" + post_par["enAdvert"] + "&queryACIP=" + post_par["queryACIP"] + \
"&loginMethod=" + post_par["loginMethod"]
r_url = r_url_head + r_url_par
return r_url
if __name__ == '__main__':
'''
联通:unicom
移动:cmcc
单宽:other
'''
#用户数据:学号 密码 运营商
login_data = ["541707090100","123456","unicom"]
# 第一个动作
URL = get_url()
# post 请求包
user_post = {
"DDDDD": "",
"upass": "",
"R1": "0",
"R2": "0",
"R3": "0",
"R6": "",
"para": "00",
"0MKKey": "123456",
"buttonClicked": "",
"redirect_url": "",
"err_flag": "",
"username": "",
"password": "",
"user": "",
"cmd": "",
"Login": ""
}
# 这里是由于不知道R6参数的含义,只能反复提交参数,有一个能命中就行了
dynamic_R6_data = ['0', '1', '2']
for i in range(len(dynamic_R6_data)):
user_post["DDDDD"] = "," + dynamic_R6_data[i] + "," + login_data[0] + "@" + login_data[2]
user_post["upass"] = login_data[1]
user_post["R6"] = dynamic_R6_data[i]
# 第二个动作
requests.post(URL, data=user_post, headers=login_headers)
import os
file_path = os.getcwd() + "\login_data.ini"
def set_login_data():
"""
配置登录信息
:return:
"""
ini_data = []
for i in range(3):
if i == 0:
ini_data.append(input("请输入上网账号:"))
if i == 1:
ini_data.append(input("请输入上网密码:"))
if i == 2:
t = input("请输入运营商:\n联通【1】\n移动【2】\n单宽【3】\n")
if t == "1":
ini_data.append("unicom")
if t == "2":
ini_data.append("cmcc")
if t == "3":
ini_data.append("other")
if len(ini_data) != 3:
print("error:input data")
return
else:
f = open(file_path, 'w')
for line in ini_data:
f.write(line + '\n')
f.close()
pass
def get_login_data():
"""
读取配置信息
:return:
"""
temp = []
try:
f = open(file_path)
for line in f.readlines():
temp.append(line.strip('\n'))
print("登录账号:"+str(temp[0]))
f.close()
return temp
except IOError:
print("login_data.ini 文件找不到")
return []
pass
if __name__ == '__main__':
if os.access(file_path, os.F_OK):
print("正在读取配置文件...")
else:
print("请按要求配置登录信息:\n")
set_login_data()
print("配置成功!正在进行登录操作...")
# 这个login_data就可以代替上面代码中自己定义的了
login_data = get_login_data()
pass
【实现打开计算机的wifi】
def open_wifi():
"""
dos命令打开wifi并链接校园网
:return:
"""
os.system('netsh wlan connect name=zzuli-student')
time.sleep(1)
【检查是否成功联网】
ps:作恶多端的百度唯一的用处就是检测网络是否联通
def is_net_ok():
"""
判断网络是否链接
:return:
"""
s = socket.socket()
s.settimeout(1)
try:
status = s.connect_ex(('www.baidu.com', 443))#
if status == 0:
s.close()
return True
else:
s.close()
return False
except Exception as e:
return False
【打包】pyinstaller -F 文件名.py
采用pyinstaller打包成exe,相关的教程遍地都是(搜索:pyinstaller打包 exe)
这个版本依旧是有很多bug的,所以我会持续更新的。