学python也有一段时间,学校7月10日就抢课了,剩下两天时间,挑战一下自己,看能不能实现。第一次写知乎,文章讲得不够详细,需要一定的python基础,废话不多说直接开始。
python配置:python3.6.5+tesseract-ocr(验证码识别)(安装不了的话可手动输入代替)+requests库
抓包工具:Charles4.5.6(百度有配置教程)
广州大学登录url:https://cas.gzhu.edu.cn/cas_server/login?service=https%3A%2F%2Fcas.gzhu.edu.cn%3A443%2Fshunt%2Findex.jsp
先来一个完整的抓包:
这么多包数据我们不是每个发送的,首先看一下首页:
没有param(QueryString)是固定的,没有cookie数据,并且Headers没有Referer指向,这里想到多人抢课网络延迟会比较大,先给requests弄个超时封装一下:
首页
url = 'https://cas.gzhu.edu.cn/cas_server/login?service=https%3A%2F%2Fcas.gzhu.edu.cn%3A443%2Fshunt%2Findex.jsp'
header = { #浏览器请求头
'User-Agent': 'Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/9.0.084.36 Safari/536.5',
}
session = requests.session() #session()cookie自动管理器
def reqs(action, this_url, datas={}, Time=30, error_tip='网络不流畅,重试中。。。'):
# 为后面的每个requests请求添加超时操作
while True:
try:
if action == 'get' or action == 'GET':
return session.get(this_url, params=datas, headers=header, timeout=Time)
elif action == 'post' or action == 'POST':
return session.post(this_url, data=datas, headers=header, timeout=Time)
break
except Exception as e:
if str(e).find('timed out') > -1:
print(error_tip)
else:
print(e)
input()
continue
request = reqs('get', url) # 进入首页
print(request.text)
看到输出的数据和抓包的response一样说明请求成功了。
接下来就要分析登录提交的form表单数据:
多登陆几次抓包就发现有些固定值不用变,帐号密码固定值的就不说了,我们要完成的数据是captcha,lt,execution。
先看captcha验证码:
验证码其实就是个图片请求,我们就get请求,本地打开验证码图片,手动输入验证码就可以了。
分析验证码请求:
有cookie信息,记住开头结尾B9........E1
这不是我们一开始请求过的首页吗?其实一开始我们请求首页的时候就使用了requests库里的session()来自动保存cookie。所以只要请求过的页面就会自动保存cookie。
再看一下验证码请求captcha的headers:
有个Referer指向,对比一下就是我们进入首页的url,在header里添加一个Referer就好了:
url = 'https://cas.gzhu.edu.cn/cas_server/login?service=https%3A%2F%2Fcas.gzhu.edu.cn%3A443%2Fshunt%2Findex.jsp'
header['Referer']=url
看一下验证码的请求url,ok,发送:
def captcha(): # 验证码处理
this_url = 'https://cas.gzhu.edu.cn/cas_server/captcha.jsp'
request = reqs('get', this_url)
with open('code.png', 'wb')as f:
f.write(request.content)
open('code.png').show()
code = input('Please input the code: ')
return code
完美下一个表单数据 lt:
直接复制该值,ctrl+F查找
只看response响应
双击打开:
这不是首页里的response数据吗,我们只要将上面请求过首页的request请求拿来用就行了。
def lt(request): # 登录form['lt']
result = re.search(r'lt" value="(.*?)"', request.text).groups()[0]
return result
个人喜欢用re来查找,lt" value="对应的是我们要的lt值的前部,而"是lt值后部,中间的(.*?)就是我们想要的内容,而.groups()[0]可以理解为提取中间(.*?)部分的固定格式。想要具体学习正则的话可以点击:python正则表达式_python正则表达式教程_python正则表达式视频教程-慕课网
同理,我们看到上面
第二条数据也出来了:
def execution(request): # 登录form['execution']
result = re.search(r'execution" value="(.*?)"', request.text).groups()[0]
return result
到这时登录表单都ok了,这里参数request是首页的请求。
form_data = {
'username': username,
'password': password,
'captcha': captcha(),
'warn': 'true',
'lt': lt(request),
'execution': execution(request),
'_eventId': 'submit',
'submit': '登录'
}
看一下提交的url:
这里ctrl+F是查不到的,对比一下发现和首页也就差了中间部分jsessionid=B9D54AE28BEE906B61A1201D87ECE6E1,我们就搜这个试试?
很nice这就在我们的首页里,直接安排:
def login_url(request): # 登录按钮链接
result = re.search(r'fm1" action="(.*?)"', request.text).groups()[0]
return 'https://cas.gzhu.edu.cn' + result
然后对比一下cookie
还是那个,问题不大。
最后看一下headers里的请求指向:
还是首页的url,完美,发送请求:
def login():
request = reqs('get', url) # 进入首页
this_url = login_url(request)
header['Referer'] = url
form_data = {
'username': username,
'password': password,
'captcha': captcha(),
'warn': 'true',
'lt': lt(request),
'execution': execution(request),
'_eventId': 'submit',
'submit': '登录'
}
request = reqs('post', this_url, form_data)
print(request.text)
记得这个页面没,看到这个就是登录成功了。
接下来进入系统:
浏览器中右击检查,记住右边的url
在charle找到这个url,发送请求:
request = reqs('get', 'http://jwxt.gzhu.edu.cn/sso/lyiotlogin')
print(request.text)
出现编码错误:
我们需要解码一下:
print(request.text.encode('gbk', 'ignore').decode('gbk'))
看到这页面就已经成功进入首页了,不过这堆数据看起来有点繁杂,我改用更简洁的方式提示我们已经进入首页,还记得我们进入官网时图片旁边的名字吗直接在抓包中搜索就行了:
因为这里param数据不是固定不变的,所以这里需要在请求是带上param数据,里面的_是13位时间戳可以通过int(time.time() * 1000)获得,并且请求头headers中Referer指向上一次,发起请求:
def get_name(req):
this_url = 'http://jwxt.gzhu.edu.cn/jwglxt/xtgl/index_cxYhxxIndex.html'
param_data = {
'xt': 'jw',
'localeKey': 'zh_CN',
'_': str(int(time.time() * 1000)),
'gnmkdm': 'index',
'su': username
}
header['Referer'] = req.url
request = reqs('get', this_url, param_data)
name = re.search(r'media-heading">(.*?)</', request.text).groups()[0]
return name
最后附上全部代码(带验证码识别,没有成功安装tesserocr的请注释掉yzm_deal.py文件中的import tesserocr,使用手动输入):
GZschool.py
import re
import requests
import time
import yzm_deal
username = '' # 验证码
password = '' # 密码
seting = 160 # 验证码识别设置的阀值
isAutoYzm = input('是否自动识别验证码(N/n取消)?')
url = 'https://cas.gzhu.edu.cn/cas_server/login?service=https%3A%2F%2Fcas.gzhu.edu.cn%3A443%2Fshunt%2Findex.jsp'
header = { # 浏览器请求头
'User-Agent': 'Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/9.0.084.36 Safari/536.5',
}
session = requests.session() # cookie管理器
def reqs(action, this_url, datas={}, Time=30, error_tip='网络不流畅,重试中。。。'):
# 请求
while True:
try:
if action == 'get' or action == 'GET':
return session.get(this_url, params=datas, headers=header, timeout=Time)
elif action == 'post' or action == 'POST':
return session.post(this_url, data=datas, headers=header, timeout=Time)
break
except Exception as e:
if str(e).find('timed out') > -1:
print(error_tip)
else:
print(e)
input()
continue
def lt(request): # 登录form['lt']
result = re.search(r'lt" value="(.*?)"', request.text).groups()[0]
return result
def execution(request): # 登录form['execution']
result = re.search(r'execution" value="(.*?)"', request.text).groups()[0]
return result
def login_url(request): # 登录按钮链接
result = re.search(r'fm1" action="(.*?)"', request.text).groups()[0]
return 'https://cas.gzhu.edu.cn' + result
def captcha(): # 验证码处理
this_url = 'https://cas.gzhu.edu.cn/cas_server/captcha.jsp'
request = reqs('get', this_url)
with open('code.png', 'wb')as f:
f.write(request.content)
yzm_deal.yzm_print(seting)
if isAutoYzm != 'N' or isAutoYzm != 'n':
code = yzm_deal.yzm_deal(seting)
print('code OCR result: ' + code)
return code
else:
code = input('Please input the code: ')
return code
def login(): # 登录表单提交
request = reqs('get', url) # 进入首页
this_url = login_url(request)
header['Referer'] = url
form_data = {
'username': username,
'password': password,
'captcha': captcha(),
'warn': 'true',
'lt': lt(request),
'execution': execution(request),
'_eventId': 'submit',
'submit': '登录'
}
reqs('post', this_url, form_data)
request = reqs('get', 'http://jwxt.gzhu.edu.cn/sso/lyiotlogin')
time.sleep(0.5)
return request
def get_name(req): # 获取学生姓名
this_url = 'http://jwxt.gzhu.edu.cn/jwglxt/xtgl/index_cxYhxxIndex.html'
param_data = {
'xt': 'jw',
'localeKey': 'zh_CN',
'_': str(int(time.time() * 1000)), # 13位的时间戳
'gnmkdm': 'index',
'su': username # 帐号
}
header['Referer'] = req.url
request = reqs('get', this_url, param_data)
name = re.search(r'media-heading">(.*?)</', request.text).groups()[0]
return name
def main():
req = login()
name = get_name(req)
print('欢迎 ' + name + ' 同学')
if __name__ == '__main__':
main()
yzm_deal.py文件
from PIL import Image
import tesserocr # 没有安装tesserocr的请注释掉
def binarizing(img, seting): # 验证码二值化处理
# 二值化,seting为控制阀值
img = img.convert("L") # 转灰度
pixdata = img.load()
w, h = img.size
for x in range(w):
for y in range(h):
if pixdata[x, y] < seting:
pixdata[x, y] = 0
else:
pixdata[x, y] = 255
return img
def yzm_deal(seting): # 验证码识别
img = binarizing(Image.open('code.png'), seting)
img.show()
img.save('code.png', quality=100)
result = tesserocr.image_to_text(img, lang='eng')
exclude_char_list = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890'
text = ''.join([x for x in result if x in exclude_char_list])
return text
def yzm_print(seting): # 打印验证码
# 手动输入打印验证码
img = binarizing(Image.open('code.png'), seting)
pixdata = img.load()
w, h = img.size
for y in range(h - 7):
for x in range(w):
if pixdata[x, y] == 0:
print('8', end='')
else:
print(' ', end='')
print()
第一篇先就到这为止吧,明天弄进去自主选课搜索课程。