Loading... # SCU抢课脚本 ## 分析过程 ![image-20211229145405750.png](http://xherlock.top/usr/uploads/2022/01/3823867445.png) ![image-20211229145254043.png](http://xherlock.top/usr/uploads/2022/01/3906328248.png) ![image-20211229145230672.png](http://xherlock.top/usr/uploads/2022/01/2969790241.png) 初步计算前两个结果相符 <img src="http://xherlock.top/usr/uploads/2022/01/3010547261.jpg" alt="Screenshot_20211229_145200" style="zoom:50%;" style=""> 找tokenvalue,最后在教务处主页找到,初步估计是每次选完刷新一次tokenvalue的值 ![image-20211229164921722.png](http://xherlock.top/usr/uploads/2022/01/2841941778.png) 这个貌似是教务处防脚本选课的机制(可能,因为这个功能在我看来没多大用处,具体对web机制很不了解), 它是模拟你查找一个老师,你正常选课时肯定不可避免地鼠标移到选课老师上面,它会利用类似hover的功能对这个老师进行一个查找,就是下图的queryTeacherJL,返回的数据也很让我迷惑,所以结合前人代码,我感觉代码加上这个应该没啥问题了(希望不会被请喝茶) ![image-20211229193239678.png](http://xherlock.top/usr/uploads/2022/01/2561421012.png) ## 代码部分 ### 设置各个接口 ![image-20220116093724749.png](http://xherlock.top/usr/uploads/2022/01/1809373317.png) ### 设置伪装请求头 ![image-20220116093826475.png](http://xherlock.top/usr/uploads/2022/01/3792397460.png) 要注意去除cookie,因为每回值不一样 ### 登陆函数 ![image-20220116094010542.png](http://xherlock.top/usr/uploads/2022/01/3182556288.png) 采用循环登录方式,错误的话不断重新来,直到登陆成功 此处采用了下载验证码图片的功能,能够模拟出我们教务系统登录的方式 对登陆时后端接受的信息分析,其中密码采取了md5加密,在函数中导入此模块,对密码加密进行传输 获取响应数据中存在“欢迎您”即 登陆成功! ### 获取课表 ~~~python # 获取选课结果课表 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数据格式化后的样式,据此找到各个部分的信息 ~~~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": {} } ] ~~~ ### 搜索函数 ~~~python # 搜索相关课程信息 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'])) ~~~ ### 获取选课信息 ```python # 获取要选课的各种信息 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 # 输入信息有误 ``` 根据不同情况返回值不同 ### 防喝茶机制 ```python # 防教务处请喝茶机制(应该是这样),它是模拟你查找一个老师,你正常选课时肯定不可避免地鼠标移到选课老师上面,它会利用类似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) ``` ### 选课函数 ```python # 选课 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)) ``` ### 主函数 ```python 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) ``` 最后修改:2022 年 01 月 16 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 0 如果觉得我的文章对你有用,请随意赞赏