SCU抢课脚本
分析过程
初步计算前两个结果相符
找tokenvalue,最后在教务处主页找到,初步估计是每次选完刷新一次tokenvalue的值
这个貌似是教务处防脚本选课的机制(可能,因为这个功能在我看来没多大用处,具体对web机制很不了解),
它是模拟你查找一个老师,你正常选课时肯定不可避免地鼠标移到选课老师上面,它会利用类似hover的功能对这个老师进行一个查找,就是下图的queryTeacherJL,返回的数据也很让我迷惑,所以结合前人代码,我感觉代码加上这个应该没啥问题了(希望不会被请喝茶)
代码部分
设置各个接口
设置伪装请求头
要注意去除cookie,因为每回值不一样
登陆函数
采用循环登录方式,错误的话不断重新来,直到登陆成功
此处采用了下载验证码图片的功能,能够模拟出我们教务系统登录的方式
对登陆时后端接受的信息分析,其中密码采取了md5加密,在函数中导入此模块,对密码加密进行传输
获取响应数据中存在“欢迎您”即 登陆成功!
获取课表
# 获取选课结果课表
def getClassTable(s):
classTable_res = s.get(url=classTable_url, headers=headers)
class_list = classTable_res.json()['xkxx']
print('您已选的所有课程如下:')
cnt = 0
print("--- {0:25} {1:8} {2}_{3}".format('课程名', '老师名', '课程号', '课序号')) # 使用format时一定不要在:间空格,否则报错,找了老长时间问题了
for i in class_list:
for j in i.values():
cnt += 1
print("[{0}] {1:25} {2:8} {3}_{4}".format(
str(cnt),
j['courseName'],
re.split(r"[*]", j['attendClassTeacher'])[0],
list(j['id'].values())[0],
list(j['id'].values())[1]
))
class_dict = {}
if j['courseName'] not in selected_class:
class_dict['kch'] = list(j['id'].values())[0]
class_dict['kxh'] = list(j['id'].values())[1]
class_dict['kcm'] = j['courseName']
selected_class.append(class_dict)
这里采用列表的数据形式接收课程信息,循环打印,并添加到字典中,为后面 检查是否已选课做准备
下面是json数据格式化后的样式,据此找到各个部分的信息
[
{
"105396020_21": {
"attendClassTeacher": "李喜雪* ",
"courseCategoryCode": "",
"courseCategoryName": "",
"courseName": "大学英语(创意阅读)-4",
"coursePropertiesCode": "001",
"coursePropertiesName": "必修",
"courseSelectionTime": "20211224012618",
"dgFlag": "81086440,李喜雪(无)",
"examTypeCode": "01",
"examTypeName": "考试",
"flag": "",
"fsktms": "",
"id": {
"coureNumber": "105396020",
"coureSequenceNumber": "21",
"executiveEducationPlanNumber": "2021-2022-2-1",
"studentNumber": "xxxxxxx" // 此处省略
},
"programPlanName": "网络空间安全培养方案",
"programPlanNumber": "8160",
"restrictedCondition": "",
"rlFlag": "81086440,李喜雪(无)",
"selectCourseStatusCode": "016",
"selectCourseStatusName": "置入",
"sfczfskt": "",
"studyModeCode": "01",
"studyModeName": "正常",
"timeAndPlaceList": [],
"unit": 2,
"xkzy": "",
"ywdgFlag": "",
"zkxh": ""
},
"106777020_01": {},
"107061050_44": {},
"107118000_12": {},
"314003030_03": {},
"314037040_03": {},
"314038030_03": {},
"314039040_01": {},
"314042020_01": {},
"314049020_01": {},
"314056030_03": {},
"999005030_05": {}
}
]
搜索函数
# 搜索相关课程信息
def search(s):
search = input('请输入你要搜索的关键字(课程名或者老师名):')
data = {
'searchtj': search,
'xq': '0',
'jc': '0',
'kclbdm': ''
}
search_json = s.post(url=search_url, data=data, headers=headers).json()
result_list = search_json['rwRxkZlList'] # 此时成为字符串,所以后续错误,因为按照列表来读取数据的
result_list = json.loads(result_list)
if not result_list: # 判断列表为空
print('无结果')
return
for item in result_list:
print('{0}----{1}----{2}_{3}----课余量:{4}'.format(item['kcm'], item['skjs'], item['kch'], item['kxh'], item['bkskyl']))
获取选课信息
# 获取要选课的各种信息
def getSelectInfo(s, kch, kxh, kcm):
data = {
'searchtj': kch,
'xq': '0',
'jc': '0',
'kclbdm': ''
}
search_json = s.post(url=search_url, data=data, headers=headers).json()
result_list = search_json['rwRxkZlList'] # 此时成为字符串,所以后续错误,因为按照列表来读取数据的
result_list = json.loads(result_list)
if not result_list: # 判断列表为空
return -2 # 已选同课程号的课
for item in result_list:
if item['kcm'] == kcm and item['kch'] == kch and item['kxh'] == kxh: # 判断输入信息无误
if int(item['bkskyl']) > 0:
return 1 # 有课余量
else:
return -1 # 无课余量
else:
return -3 # 输入信息有误
根据不同情况返回值不同
防喝茶机制
# 防教务处请喝茶机制(应该是这样),它是模拟你查找一个老师,你正常选课时肯定不可避免地鼠标移到选课老师上面,它会利用类似hover的功能对这个老师进行一个查找
def queryTeacherJL(s, kch, kxh):
query_data = {
'id': '2021-2022-1@' + kch + '@' + kxh
}
return s.post(url=query_url, data=query_data, headers=headers)
选课函数
# 选课
def select(s):
kch = input('请输入课程号(9位数字):')
kxh = input('请输入课序号(01、02等):')
kcm = input('请输入课程名(完整的):')
new_kcms = kcm + kch + '@' + kxh
kcms = ''
for i in new_kcms:
kcms = kcms + str(ord(i)) + ','
cnt = 0
while True:
if not queryTeacherJL(s, kch, kxh):
return # 及时退出
cnt += 1
print('第{0}轮选课:'.format(cnt))
select_result = getSelectInfo(s, kch, kxh, kcm)
if select_result == 1:
token_res = s.get(url=token_url, headers=headers).text
tokenvalue = re.compile("([a-fA-F0-9]{32})").findall(token_res)[0]
select_data = {
'dealType': '5',
'kcIds': kch + '@' + kxh + '@2021-2022-2-1',
# 分析:大的数应该是中文的unicode编码,小的是课程号和课序号编码
# 'kcms': '20154, 31867, 30340, 35821, 35328, 45, 45, 35821, 35328, 23398, 20837, 38376, 40, 49, 48, 53, 50, 57, 57, 48, 50, 48, 64, 48, 49, 41',
# 人类的语言--语言学入门(105299020_01)
# 数码摄影科学与艺术(205300020_01)
# 25968, 30721, 25668, 24433, 31185, 23398, 19982, 33402, 26415, 40, 50, 48, 53, 51, 48, 48, 48, 50, 48, 64, 48, 49, 41,
# 经验证确实如此,这些数字是Unicode的10进制编码
'kcms': kcms,
'fajhh': '8160',
'sj': '0_0',
'searchtj': '',
'kclbdm': '',
'inputCode': '',
'tokenValue': tokenvalue
}
select_res = s.post(url=select_url, data=select_data, headers=headers).json()
print(select_res)
getClassTable(s)
break
elif select_result == -1:
print('无课余量')
elif select_result == -2:
print('已选同课程号的课或者课程号错误')
return
elif select_result == -3:
print('输入信息有误')
return
time.sleep(random.uniform(1.5, 3))
主函数
def run(s):
login(s)
print(Fore.LIGHTRED_EX + "*****欢迎来到Xherlock的选课系统*****")
print(Fore.LIGHTRED_EX + " (1) 查看课表")
print(Fore.LIGHTRED_EX + " (2) 查询课程信息")
print(Fore.LIGHTRED_EX + " (3) 选课")
print(Fore.LIGHTRED_EX + " (4) 退出")
print(Style.RESET_ALL)
while True:
choice = input('请输入选项:')
if choice == '1':
getClassTable(s)
elif choice == '2':
search(s)
elif choice == '3':
select(s)
elif choice == '4':
quit()
def quit():
print(Fore.LIGHTBLUE_EX + "感谢您使用Xherlock的文件管理系统!")
sys.exit(0)
if __name__ == '__main__':
# ASCII字符签名,好看而已
print(Fore.LIGHTMAGENTA_EX + "___ ___ __ __ _______ .______ __ ______ ______ __ ___")
print("{0}\\ \\ / / | | | | | ____|| _ \\ | | / __ \\ / || |/ /".format(Fore.LIGHTMAGENTA_EX))
print("{0} \\ V / | |__| | | |__ | |_) | | | | | | | | ,----\'| \' / ".format(Fore.LIGHTMAGENTA_EX))
print("{0} > < | __ | | __| | / | | | | | | | | | < ".format(Fore.LIGHTMAGENTA_EX))
print("{0} / . \\ | | | | | |____ | |\\ \\----.| `----.| `--\' | | `----.| . \\ ".format(Fore.LIGHTMAGENTA_EX))
print("{0}/__/ \\__\\ |__| |__| |_______|| _| `._____||_______| \\______/ \\______||__|\\__\\".format(Fore.LIGHTMAGENTA_EX))
print(Style.RESET_ALL)
session = requests.session() # 保存cookie
run(session)