
继续昨天的内容,如果没看第一篇的同学可以进入我的文章查看。今天学校很配合开放了查看课程搜索,给了我抓包的机会,不过抓包后分析的数据弄了很久时间。今天的任务是:进入自主选课,并完成搜索课程。完成的任务结果是这样的:

这就是我们搜索课程 世界电影大师 的返回结果,这些结果数据在提交抢课篇是必须用到的,也就是说能爬到这里的话也就差最后一步提交抢课了。
废话不多说,抓包走一波:


推荐按照我的顺序抓,每一次操作留一段时间来标记下一步操作,最好不要有操作失误,不然抓包数据看起来很乱。
这里先讲一下上一篇没讲的302和200的区分:

假如我们的post请求的是302,它就会不断自动重定向直至出现200的状态码。一般我们只分析返回状态码是200的数据,记住是返回的response数据,发送的request请求除外

中间一个蓝色圆圈的是网页,显示网页基本内容
js图标的就是js代码,用作动态数据的,比如我们的课程点击搜索才出现在下面用的就是js生成
ok正篇开始:
进入自主选课
charles:

为什么不看第一个post,因为第一个post数据只是返回一个提示,并且没有set-cookie数据,可以略过。这里先看params参数:default多加载几次发现是一样的,su帐号不用说了,
N253512其实是识别你进入自主选课的参数,ctrl+f就出来了,就在我们的系统页面中:

代码:
gnmkdm = re.search(r"'(.{1,8})','(.{10,50})','自主选课", req.text).groups()
然后看cookie:

和前面不同了,安装上一篇的方法,选到response往上找哪里有set-cookie数据

看到这个cookie跟我们的一样,再仔细看一看,这不是在我们上一篇登录进入系统重定向过程中的一个请求吗,已经自动保存在我们session()里面的,所以cookie不用管。
看一下headers请求头:


发现就是进入系统的页面,用该页面的request.url就可以提取,这里要讲一下怎么知道发送请求的url:

问号后面就是我们要发送的param参数,?号前面的链接就是我们的this_url。这里我的进入自主选课前面的部分链接也是提取出来的,觉得麻烦直接写死也没问题。
代码:
def enter_choose(req):
global enter_choose_num
global ver
gnmkdm = re.search(r"'(.{1,8})','(.{10,50})','自主选课", req.text).groups()
enter_choose_num = gnmkdm[0]
this_url = 'http://jwxt.gzhu.edu.cn/jwglxt' + gnmkdm[1]
param_data = {
'gnmkdm': gnmkdm[0],
'layout': 'default',
'su': username
}
header['Referer'] = req.url
request = reqs('get', this_url, param_data=param_data)
return request
进入选课页面后,就要进行我们今天的重头戏,搜索课程:
主修课程搜索:

看到表单数据没,看到都想吐,没办法一个一个分析。这里介绍一个工具,不想逐个输入字段名并且你的浏览器是谷歌的话,可以按F12选中该请求右击——copy——copy as cURL(base),然后进入这个网址:https://curl.trillworks.com/

这样请求头,form表单,还有param参数等等所有数据都帮你写好了。咦?等等,这不就可以......嘿嘿
看到这么长的数据随便挑一个最长的搜索把:

很nice仔细对照一下发现里面有一大部分数据都有出现在表单里,而且这请求就是我们进入选课的请求,写个函数,输入字段和请求返回结果。
def get_input_value(id, req):
result = re.search(str(id) + '"' + '.*?value="(.*?)"', req.text).groups()[0]
return result
而另外一大部分的数据在这个请求里:

xkkz_id搜索一下他的值发现在也是在进入选课的页面
xszxztz一样
kspage和jspage找的方法不一样,直接搜索他们的名字:

找到他们的名字在js中它们的初始值为0,看一下这个请求发送了什么

啥也没有,cookie一样,20191204在之前随便一个请求都能获得,再弄一下header指向:
def get_hidden_data_form(req): # 获取kspage和jspage
this_url = 'http://jwxt.gzhu.edu.cn/jwglxt/js/comp/jwglxt/xkgl/xsxk/zzxkYzb.js'
ver = re.search(r'ver=(.*?)"', req.text).groups()[0] # 获取ver的参数
param_data = {
'ver': ver
}
header['Referer'] = req.url
request = reqs('get', this_url, param_data=param_data)
result = json.loads('{"' + re.search(r'kspageD*d*D*d*', request.text).group() + '}')
return result
def get_hidden_data(req, num): # 获取另一部分form表单请求
this_url = 'http://jwxt.gzhu.edu.cn/jwglxt/xsxk/zzxkyzb_cxZzxkYzbDisplay.html'
page_data = get_hidden_data_form(req)
xkkz_id = search_form_kklxdm(req, num)[1]
form_data = {
'xkkz_id': xkkz_id,
'xszxzt': get_input_value('xszxzt', req),
'kspage': page_data['kspage'],
'jspage': page_data['jspage']
}
param_data = {
'gnmkdm': enter_choose_num,
'su': username
}
header['Referer'] = req.url
request = reqs('post', this_url, param_data=param_data, form_data=form_data)
return page_data, request

最后就剩下这三个数据:
kklxdm:

在js找到kklxdm被赋值为firstKklxdm这个元素的值,搜索该值:

代码:
def search_form_kklxdm(req):
result = re.findall(r'queryCourse.*?,(.*?))', req.text)
return eval('[' + result[0] + ']')
kspage和jspage:

看到这堆数据都是我们要的表单数据,kspage就是初始值+1,而jspage=初始值+step,我们在这个js里搜索一下step:

完美,表单数据就这样填满一个了,接下来就弄一下referer指向和param参数:
def search(req): # req 是首页的请求
this_url = 'http://jwxt.gzhu.edu.cn/jwglxt/xsxk/zzxkyzb_cxZzxkYzbPartDisplay.html'
param_data = {
'gnmkdm': enter_choose_num,
'su': username
}
page_data, req2 = get_hidden_data(req) # 获取请求2
kklxdm = search_form_kklxdm(req) # 查找kklxdm
form_data = {
'filter_list[0]': lesson_name, # ok
'xqh_id': get_input_value('xqh_id', req), # ok
'jg_id': get_input_value('jg_id_1', req), # ok-------------
'zyh_id': get_input_value('zyh_id', req), # ok
'zyfx_id': get_input_value('zyfx_id', req), # ok
'njdm_id': get_input_value('njdm_id', req), # ok
'bh_id': get_input_value('bh_id', req), # ok
'xbm': get_input_value('xbm', req), # ok
'xslbdm': get_input_value('xslbdm', req), # ok
'ccdm': get_input_value('ccdm', req), # ok
'xsbj': get_input_value('xsbj', req), # ok
'xkxnm': get_input_value('xkxnm', req), # ok
'xkxqm': get_input_value('xkxqm', req), # ok
'jxbzb': get_input_value('jxbzb', req), # ok
'sfkknj': get_input_value('sfkknj', req2), # ok
'sfkkzy': get_input_value('sfkkzy', req2), # ok
'sfznkx': get_input_value('sfznkx', req2), # ok
'zdkxms': get_input_value('zdkxms', req2), # ok
'sfkxq': get_input_value('sfkxq', req2), # ok
'sfkcfx': get_input_value('sfkcfx', req2), # ok
'kkbk': get_input_value('kkbk', req2), # ok
'kkbkdj': get_input_value('kkbkdj', req2), # ok
'sfkgbcx': get_input_value('sfkgbcx', req2), # ok
'sfrxtgkcxd': get_input_value('sfrxtgkcxd', req2), # ok
'tykczgxdcs': get_input_value('tykczgxdcs', req2), # ok
'rlkz': get_input_value('rlkz', req2), # ok
'rwlx': get_input_value('rwlx', req2), # ok
'xkly': get_input_value('xkly', req2), # ok
'bklx_id': get_input_value('bklx_id', req2), # ok
'kklxdm': kklxdm[0], # ok-------------
'kspage': str(int(page_data['jspage']) + 1), # -------
'jspage': str(int(page_data['jspage']) + 10), # -------
}
header['Referer'] = req.url
request = reqs('post', this_url, param_data=param_data, form_data=form_data)
选修课程搜索:
接下来下一个请求:

一大堆隐藏域,很明显为下一个请求使用的,发送请求:

和之前的请求几乎一样,通过比较只有xkkz_id不同,并且在上面求01时出现过一次该请求:

很明显是区分主修课程和选修课程用的,修改一下刚刚的请求根据不同的选择返回不同的值:
def search_form_kklxdm(req, num):
result = re.findall(r'queryCourse.*?,(.*?))', req.text)
if num == 1:
return eval('[' + result[0] + ']')·
else:
return eval('[' + result[1] + ']')
def get_hidden_data(req, num):
this_url = 'http://jwxt.gzhu.edu.cn/jwglxt/xsxk/zzxkyzb_cxZzxkYzbDisplay.html'
page_data = get_hidden_data_form(req)
xkkz_id = search_form_kklxdm(req, num)[1]
form_data = {
'xkkz_id': xkkz_id,
'xszxzt': get_input_value('xszxzt', req),
'kspage': page_data['kspage'],
'jspage': page_data['jspage']
}
param_data = {
'gnmkdm': enter_choose_num,
'su': username
}
header['Referer'] = req.url
request = reqs('post', this_url, param_data=param_data, form_data=form_data)
return page_data, request
下一个请求,对比一下主修课程的form表单,发现字段名基本都相同,只不过的其中一部分需要用到刚刚请求过的隐藏域,修改一下,设置成可以选择主修或者选修课程的不同表单发送:
def search(req):
this_url = 'http://jwxt.gzhu.edu.cn/jwglxt/xsxk/zzxkyzb_cxZzxkYzbPartDisplay.html'
lesson_name = input('n请输入课程名称:') # ok------------
lesson_choose = input('n主修课程请按"1",选修课程请按"2":')
param_data = {
'gnmkdm': enter_choose_num,
'su': username
}
if lesson_choose == '1':
page_data, req2 = get_hidden_data(req, 1) # 1就是主修课程
kklxdm = search_form_kklxdm(req, 1)
else:
page_data, req2 = get_hidden_data(req, 2) # 1就是主修课程
kklxdm = search_form_kklxdm(req, 2)
form_data = {
'filter_list[0]': lesson_name, # ok
'xqh_id': get_input_value('xqh_id', req), # ok
'jg_id': get_input_value('jg_id_1', req), # ok-------------
'zyh_id': get_input_value('zyh_id', req), # ok
'zyfx_id': get_input_value('zyfx_id', req), # ok
'njdm_id': get_input_value('njdm_id', req), # ok
'bh_id': get_input_value('bh_id', req), # ok
'xbm': get_input_value('xbm', req), # ok
'xslbdm': get_input_value('xslbdm', req), # ok
'ccdm': get_input_value('ccdm', req), # ok
'xsbj': get_input_value('xsbj', req), # ok
'xkxnm': get_input_value('xkxnm', req), # ok
'xkxqm': get_input_value('xkxqm', req), # ok
'jxbzb': get_input_value('jxbzb', req), # ok
'sfkknj': get_input_value('sfkknj', req2), # ok
'sfkkzy': get_input_value('sfkkzy', req2), # ok
'sfznkx': get_input_value('sfznkx', req2), # ok
'zdkxms': get_input_value('zdkxms', req2), # ok
'sfkxq': get_input_value('sfkxq', req2), # ok
'sfkcfx': get_input_value('sfkcfx', req2), # ok
'kkbk': get_input_value('kkbk', req2), # ok
'kkbkdj': get_input_value('kkbkdj', req2), # ok
'sfkgbcx': get_input_value('sfkgbcx', req2), # ok
'sfrxtgkcxd': get_input_value('sfrxtgkcxd', req2), # ok
'tykczgxdcs': get_input_value('tykczgxdcs', req2), # ok
'rlkz': get_input_value('rlkz', req2), # ok
'rwlx': get_input_value('rwlx', req2), # ok
'xkly': get_input_value('xkly', req2), # ok
'bklx_id': get_input_value('bklx_id', req2), # ok
'kklxdm': kklxdm[0], # ok-------------
'kspage': str(int(page_data['jspage']) + 1), # -------
'jspage': str(int(page_data['jspage']) + 10), # -------
}
header['Referer'] = req.url
request = reqs('post', this_url, param_data=param_data, form_data=form_data)
你会发现选修课程搜索会出现两个post数据,并且第二个发送的内容和返回的内容都不同,那就都post一下把,保存这些数据明天用,大同小异,这里就不讲解了,今天的任务也算完成了。最后附上代码(把昨天的修改了一下验证码不能手动输入的错误。写到凌晨两点,太困了,后面的都很潦草过了,不懂的可以在评论区或者私聊我):
GZschool.py
import re
import requests
import time
import yzm_deal
import json
username = input('帐号:')
password = input('密码:')
seting = 160
isAutoYzm = input('n是否自动识别验证码(N/n取消): ')
enter_choose_num = '' # 进入选修课的代号
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()
def reqs(action, this_url, param_data={}, form_data={}, Time=30, error_tip='网络不流畅,重试中。。。'):
# 请求
while True:
try:
if action == 'get' or action == 'GET':
return session.get(this_url, params=param_data, headers=header, timeout=Time)
elif action == 'post' or action == 'POST':
return session.post(this_url, params=param_data, data=form_data, 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 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' and 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 = 'https://cas.gzhu.edu.cn' + re.search(r'fm1" action="(.*?)"', request.text).groups()[0] # 登录按钮链接
lt = re.search(r'lt" value="(.*?)"', request.text).groups()[0] # 登录form['lt']
execution = re.search(r'execution" value="(.*?)"', request.text).groups()[0] # 登录form['execution']
form_data = {
'username': username,
'password': password,
'captcha': captcha(),
'warn': 'true',
'lt': lt,
'execution': execution,
'_eventId': 'submit',
'submit': '登录'
}
header['Referer'] = url
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)),
'gnmkdm': 'index',
'su': username
}
header['Referer'] = req.url
request = reqs('get', this_url, param_data=param_data)
name = re.search(r'media-heading">(.*?)</', request.text).groups()[0]
return name
def enter_choose(req):
global enter_choose_num
global ver
gnmkdm = re.search(r"'(.{1,8})','(.{10,50})','自主选课", req.text).groups()
enter_choose_num = gnmkdm[0]
this_url = 'http://jwxt.gzhu.edu.cn/jwglxt' + gnmkdm[1]
param_data = {
'gnmkdm': gnmkdm[0],
'layout': 'default',
'su': username
}
header['Referer'] = req.url
request = reqs('get', this_url, param_data=param_data)
return request
def get_input_value(id, req):
result = re.search(str(id) + '"' + '.*?value="(.*?)"', req.text).groups()[0]
return result
def get_hidden_data_form(req):
this_url = 'http://jwxt.gzhu.edu.cn/jwglxt/js/comp/jwglxt/xkgl/xsxk/zzxkYzb.js'
ver = re.search(r'ver=(.*?)"', req.text).groups()[0]
param_data = {
'ver': ver
}
header['Referer'] = req.url
request = reqs('get', this_url, param_data=param_data)
result = json.loads('{"' + re.search(r'kspageD*d*D*d*', request.text).group() + '}')
return result
def get_hidden_data(req, num):
this_url = 'http://jwxt.gzhu.edu.cn/jwglxt/xsxk/zzxkyzb_cxZzxkYzbDisplay.html'
page_data = get_hidden_data_form(req)
xkkz_id = search_form_kklxdm(req, num)[1]
form_data = {
'xkkz_id': xkkz_id,
'xszxzt': get_input_value('xszxzt', req),
'kspage': page_data['kspage'],
'jspage': page_data['jspage']
}
param_data = {
'gnmkdm': enter_choose_num,
'su': username
}
header['Referer'] = req.url
request = reqs('post', this_url, param_data=param_data, form_data=form_data)
return page_data, request
def search_form_kklxdm(req, num):
result = re.findall(r'queryCourse.*?,(.*?))', req.text)
if num == 1:
return eval('[' + result[0] + ']')
else:
return eval('[' + result[1] + ']')
def search(req):
this_url = 'http://jwxt.gzhu.edu.cn/jwglxt/xsxk/zzxkyzb_cxZzxkYzbPartDisplay.html'
lesson_name = input('n请输入课程名称:') # ok------------
lesson_choose = input('n主修课程请按"1",选修课程请按"2":')
param_data = {
'gnmkdm': enter_choose_num,
'su': username
}
if lesson_choose == '1':
page_data, req2 = get_hidden_data(req, 1) # 1就是主修课程
kklxdm = search_form_kklxdm(req, 1)
else:
page_data, req2 = get_hidden_data(req, 2) # 2就是选修课程
kklxdm = search_form_kklxdm(req, 2)
form_data = {
'filter_list[0]': lesson_name, # ok
'xqh_id': get_input_value('xqh_id', req), # ok
'jg_id': get_input_value('jg_id_1', req), # ok-------------
'zyh_id': get_input_value('zyh_id', req), # ok
'zyfx_id': get_input_value('zyfx_id', req), # ok
'njdm_id': get_input_value('njdm_id', req), # ok
'bh_id': get_input_value('bh_id', req), # ok
'xbm': get_input_value('xbm', req), # ok
'xslbdm': get_input_value('xslbdm', req), # ok
'ccdm': get_input_value('ccdm', req), # ok
'xsbj': get_input_value('xsbj', req), # ok
'xkxnm': get_input_value('xkxnm', req), # ok
'xkxqm': get_input_value('xkxqm', req), # ok
'jxbzb': get_input_value('jxbzb', req), # ok
'sfkknj': get_input_value('sfkknj', req2), # ok
'sfkkzy': get_input_value('sfkkzy', req2), # ok
'sfznkx': get_input_value('sfznkx', req2), # ok
'zdkxms': get_input_value('zdkxms', req2), # ok
'sfkxq': get_input_value('sfkxq', req2), # ok
'sfkcfx': get_input_value('sfkcfx', req2), # ok
'kkbk': get_input_value('kkbk', req2), # ok
'kkbkdj': get_input_value('kkbkdj', req2), # ok
'sfkgbcx': get_input_value('sfkgbcx', req2), # ok
'sfrxtgkcxd': get_input_value('sfrxtgkcxd', req2), # ok
'tykczgxdcs': get_input_value('tykczgxdcs', req2), # ok
'rlkz': get_input_value('rlkz', req2), # ok
'rwlx': get_input_value('rwlx', req2), # ok
'xkly': get_input_value('xkly', req2), # ok
'bklx_id': get_input_value('bklx_id', req2), # ok
'kklxdm': kklxdm[0], # ok-------------
'kspage': str(int(page_data['jspage']) + 1), # -------
'jspage': str(int(page_data['jspage']) + 10), # -------
}
header['Referer'] = req.url
request = reqs('post', this_url, param_data=param_data, form_data=form_data)
if lesson_choose != '1':
this_url2 = 'http://jwxt.gzhu.edu.cn/jwglxt/xsxk/zzxkyzb_cxJxbWithKchZzxkYzb.html'
kch_id = re.search(r'kch_id":"(d*)', request.text).groups()[0]
cxbj = re.search(r'cxbj":"(d*)', request.text).groups()[0]
fxbj = re.search(r'fxbj":"(d*)', request.text).groups()[0]
form_data2 = {
'filter_list[0]': lesson_name, # ok
'xqh_id': get_input_value('xqh_id', req), # ok
'jg_id': get_input_value('jg_id_1', req), # ok
'zyh_id': get_input_value('zyh_id', req), # ok
'zyfx_id': get_input_value('zyfx_id', req), # ok
'njdm_id': get_input_value('njdm_id', req), # ok
'bh_id': get_input_value('bh_id', req), # ok
'xbm': get_input_value('xbm', req), # ok
'xslbdm': get_input_value('xslbdm', req), # ok
'ccdm': get_input_value('ccdm', req), # ok
'xsbj': get_input_value('xsbj', req), # ok
'xkxnm': get_input_value('xkxnm', req), # ok
'xkxqm': get_input_value('xkxqm', req), # ok
'sfkknj': get_input_value('sfkknj', req2), # ok
'sfkkzy': get_input_value('sfkkzy', req2), # ok
'sfznkx': get_input_value('sfznkx', req2), # ok
'zdkxms': get_input_value('zdkxms', req2), # ok
'sfkxq': get_input_value('sfkxq', req2), # ok
'sfkcfx': get_input_value('sfkcfx', req2), # ok
'kkbk': get_input_value('kkbk', req2), # ok
'kkbkdj': get_input_value('kkbkdj', req2), # ok
'rwlx': get_input_value('rwlx', req2), # ok
'xkly': get_input_value('xkly', req2), # ok
'bklx_id': get_input_value('bklx_id', req2), # ok
'rlkz': get_input_value('rlkz', req2), # ok
'kklxdm': kklxdm[0], # ok--------------
'kch_id': kch_id,
'xkkz_id': kklxdm[1],
'cxbj': cxbj,
'fxbj': fxbj
}
header['Referer'] = req.url
request = reqs('post', this_url2, param_data=param_data, form_data=form_data2)
print(request.text)
def main():
req = login()
name = get_name(req)
print('n欢迎 ' + name + ' 同学')
req = enter_choose(req) # 进入自主选课界面
req = search(req) # 搜索课程界面
if __name__ == '__main__':
main()
yzm_deal.py:
from PIL import Image
import 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.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()