├── README.md ├── vpnsqk.py ├── 登录获取cookies.ipynb └── 抢课爬虫.ipynb /README.md: -------------------------------------------------------------------------------- 1 | # fuck_course 2 | 3 | 4 | **2021.3.21 新加入通过登录自动获取cookies,需要先登录vpns.jlu.edu.cn,再登录[vpns的教务系统](https://vpns.jlu.edu.cn/https/77726476706e69737468656265737421e5fe4c8f693a6445300d8db9d6562d/ntms/),目前整体登录逻辑较为完善,包括了验证码过期及验证码失败等判断处理,其中uims登录时 需要js逆向得到编码逻辑,在代码中已体现。至此已具备做成gui界面并简便操作的所有因素** 5 | 6 | > **吉林大学抢课爬虫**,基于`vpns.jlu.edu.cn`,不是`uims.jlu.edu.cn`,(两者并无差别,只是vpns可以校外登陆),所以获取cookie时必须在[vpns的教务系统](https://vpns.jlu.edu.cn/https/77726476706e69737468656265737421e5fe4c8f693a6445300d8db9d6562d/ntms/)上获取。基于vpns页面的好处是捡漏课程时可以挂在服务器上,且便于校外操作。 7 | 8 | 9 | 10 | 11 | 12 | 13 | ## 参数及函数说明: 14 | 15 | 1. cookie: 登陆[教务系统](https://vpns.jlu.edu.cn/https/77726476706e69737468656265737421e5fe4c8f693a6445300d8db9d6562d/ntms/)时返回的cookie 16 | 17 | 2. get_headers(cookie) 18 | 19 | 根据cookies构建消息请求头 20 | 21 | - 返回值: 22 | 23 | myheaders:根据cookies构造好的请求头 24 | 25 | 3. get_course(myheaders) 26 | 27 | 用于获取加入选课页面课程的课程id,根据此id提交post请求抢课 28 | 29 | - 参数: 30 | 31 | myheaders:get_headers(cookie)所返回的请求头 32 | 33 | - 返回值: 34 | 35 | pd.DataFrame.from_dict(course_list): 已选以及未选的课程列表 36 | 37 | course_map: 未选的课程以及编号,该编号用于选课 38 | 39 | 4. fuck_course(lsltId_list, course_map, myheaders) 40 | 41 | 根据lsltId_list中的课程id进行抢课 42 | 43 | - 参数: 44 | 45 | lsltId_list:所有待抢课程id的列表 46 | 47 | course_map:get_course(myheaders)返回的course_map 48 | 49 | myheaders:get_headers(cookie)所返回的请求头 50 | 51 | 52 | 53 | ## 具体用法: 54 | 55 | ```python 56 | cookie = 'wengine_vpn_ticket=................; refresh=1' # ...为你自己的cookie内容 57 | myheaders = get_headers(cookie) # 获取请求头 58 | df_course, course_map = get_course(myheaders) # 获取待抢课程编号 59 | print(course_map) # 输出course_map,根据其内容选择要抢的课程所对应的编号 60 | lsltId_list = [71575961, 71978271] # 要抢的课程列表 61 | fuck_course(lsltId_list, course_map, myheaders) # 开始抢课 62 | ``` 63 | 64 | 65 | 66 | 67 | 68 | ## 注意事项 69 | 70 | 1. cookie暂时需要手工登陆[vpns的教务系统](https://vpns.jlu.edu.cn/https/77726476706e69737468656265737421e5fe4c8f693a6445300d8db9d6562d/ntms/)获取 71 | 72 | 2. ```python 73 | course_dict = { 74 | 'tag': "lessonSelectLog@selectStore", 75 | 'branch': "default", 76 | 'params': {'splanId': 20} # 77 | } 78 | ``` 79 | 80 | 此为get_course(myheaders)中的代码段,其中的'splanId'可能随着不同学期选课发生变化,需要自行获取,例如20年下学期2月份值为10,20年上学期7月份值为20,21年1月抢课时为30 81 | 82 | 3. 抢课的提交网址经过三学期的测试发现是不变的,只有上一条中splanId会每学期发生变化,此处自己抓包修改即可。 83 | 84 | 4. 附:测试用例以及部分错误 85 | 86 | ```python 87 | # 测试用例以及部分错误 88 | course_detail = { 89 | 'lsltId': "70228928", 90 | 'opType': "N" # Y选课, N退课 91 | } 92 | detail_json = json.dumps(course_detail) 93 | course_html = 'https://vpns.jlu.edu.cn/https/77726476706e69737468656265737421e5fe4c8f693a6445300d8db9d6562d/ntms/action/select/select-lesson.do?vpn-12-o2-uims.jlu.edu.cn' 94 | for i in range(1,3): 95 | try: 96 | result = requests.post(course_html, data = detail_json, headers = myheaders, allow_redirects=True, verify=False) 97 | info = json.loads(result.text) 98 | if info['count'] == 1: 99 | print('第' + str(i) + '次:抢课成功') # 可用于捡漏 100 | break 101 | else: 102 | print('第' + str(i) + '次:失败 原因:'+ info['msg']) 103 | except Exception as e: # 可能服务器突然没响应 104 | print(e) 105 | time.sleep(5) # 自动调速 106 | 107 | 108 | ''' 109 | {"count":0,"errno":2080,"items":null,"msg":"班级人数已经到达上限","status":-12} 110 | {"count":1,"errno":0,"items":["S"],"msg":"","status":0} 111 | {"count":0,"errno":1995,"items":null,"msg":"上课时间冲突,不能选课","status":-6} 112 | {'count': 0, 'errno': -3, 'items': None, 'msg': '权限不足,您不能执行此操作或者获取数据', 'status': -3} 113 | {'count': 0, 'errno': 1931, 'items': None, 'msg': '尚未开始选课或者阶段不对', 'status': -12} 114 | 115 | {'applyPlanLesson': {'aplId': 524009, 116 | 'planDetail': {'credit': 2}, # 学分 117 | 'testForm': 3091, 118 | 'selectType': 3065, # 选课类型 119 | 'isReselect': 'N'}, # 选否 120 | 'notes': None, # 选课反馈 121 | 'selLssgCnt': 0, 122 | 'replyTag': None, # 反馈备注 123 | 'selectResult': 'N', # 选否 124 | 'lslId': 50245283, 125 | 'lesson': {'totalClassHour': 30, 126 | 'teachSchool': {'schoolName': '通信工程学院'}, 127 | 'lessonId': 67787, 128 | 'courseInfo': {'courType2': 3040, 'courseId': 27121, 'courName': '医用机器人专题'}}, 129 | 'sumLssgCnt': 1} 130 | ''' 131 | ``` 132 | 133 | -------------------------------------------------------------------------------- /vpnsqk.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import warnings 3 | import json 4 | import time 5 | import pandas as pd 6 | 7 | 8 | def get_headers(cookie): 9 | ''' 10 | 根据cookies构建消息请求头 11 | ''' 12 | myheaders = { 13 | 'Content-Type': 'application/json', 14 | 'cookie': cookie, 15 | 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36', 16 | 'vpn-12-o2-uims.jlu.edu.cn': '', 17 | } 18 | return myheaders 19 | 20 | 21 | def get_course(myheaders): 22 | ''' 23 | return: 24 | pd.DataFrame.from_dict(course_list): 已选以及未选的课程列表 25 | course_map: 未选的课程以及编号,该编号用于选课 26 | ''' 27 | 28 | course_html = 'https://vpns.jlu.edu.cn/https/77726476706e69737468656265737421e5fe4c8f693a6445300d8db9d6562d/ntms/service/res.do?vpn-12-o2-uims.jlu.edu.cn' 29 | deHtml = 'https://vpns.jlu.edu.cn/https/77726476706e69737468656265737421e5fe4c8f693a6445300d8db9d6562d/ntms/service/res.do?vpn-12-o2-uims.jlu.edu.cn' 30 | course_dict = { 31 | 'tag': "lessonSelectLog@selectStore", 32 | 'branch': "default", 33 | 'params': {'splanId': 20} # 注意,此参数每学期不同会发生变化,不一定一直适用!!需要进行修改维护!!通过分析选课的请求可以得到!! 34 | } 35 | deInfo = { 36 | 'tag': "lessonSelectLogTcm@selectGlobalStore", 37 | 'branch': "self", 38 | 'params': {'lslId': 0, 'myCampus': "Y"} # Y选课, N退课 39 | } 40 | type_dict = {3060: '必修课', 3061: '选修课', 3062: '限选课', 3063: '未知', 3064: '选修课', 3065: '校选修课'} 41 | course_list = [] 42 | course_map = {} 43 | 44 | # 获取所有课程 45 | course_json = json.dumps(course_dict) 46 | course_result = requests.post(course_html, data=course_json, headers=myheaders, allow_redirects=True, verify=False) 47 | course_detail = json.loads(course_result.text) 48 | 49 | # 根据获取的json格式数据转存数据 50 | for i in range(len(course_detail['value'])): 51 | single = {} 52 | single['课程名称'] = course_detail['value'][i]['lesson']['courseInfo']['courName'] 53 | single['选否'] = course_detail['value'][i]['selectResult'] 54 | single['选课类型'] = type_dict[course_detail['value'][i]['applyPlanLesson']['selectType']] 55 | lslId = course_detail['value'][i]['lslId'] 56 | deInfo['params']['lslId'] = lslId 57 | de_json = json.dumps(deInfo) 58 | de_result = requests.post(deHtml, data=de_json, headers=myheaders, allow_redirects=True, verify=False) 59 | de = json.loads(de_result.text) 60 | single['选课编号'] = de['value'][0]['lsltId'] 61 | course_list.append(single) 62 | if course_detail['value'][i]['selectResult'] == 'N': 63 | course_map[single['选课编号']] = single['课程名称'] # 仅存储未选课程的编号,其他可以自行查看 64 | time.sleep(0.5) # 调速,防止被封 65 | return pd.DataFrame.from_dict(course_list), course_map 66 | 67 | 68 | def fuck_course(lsltId_list, course_map, myheaders): 69 | ''' 70 | 根据lsltId_list中的课程id进行抢课 71 | ''' 72 | 73 | course_detail = { 74 | 'lsltId': 0, 75 | 'opType': "Y" # Y选课, N退课 76 | } 77 | course_html = 'https://vpns.jlu.edu.cn/https/77726476706e69737468656265737421e5fe4c8f693a6445300d8db9d6562d/ntms/action/select/select-lesson.do?vpn-12-o2-uims.jlu.edu.cn' 78 | i = 1 79 | while True: # 抢课轮数,暂未设置所有课程抢课成功后终止,需要手动终止 80 | try: 81 | for lsltId in lsltId_list: # 抢每一门课 82 | course_detail['lsltId'] = lsltId 83 | detail_json = json.dumps(course_detail) 84 | result = requests.post(course_html, data=detail_json, headers=myheaders, allow_redirects=True, 85 | verify=False) 86 | info = json.loads(result.text) 87 | print(info) 88 | if info['count'] == 1: 89 | print('第' + str(i) + '次:' + str(course_map[str(lsltId)]) + ' 抢课成功') # 可用于捡漏 90 | else: 91 | print('第' + str(i) + '次:' + str(course_map[str(lsltId)]) + ' 抢课失败 原因:' + info['msg']) 92 | # time.sleep(1) ###################### 调速 ###################### 93 | i += 1 94 | except Exception as e: # 可能服务器突然没响应 95 | print(e) 96 | 97 | 98 | def main(): 99 | cookie = 'wengine_vpn_ticket=33da71016633c03c; refresh=1' # ...为你自己的cookie内容 100 | myheaders = get_headers(cookie) # 获取请求头 101 | df_course, course_map = get_course(myheaders) # 获取待抢课程编号 102 | print(course_map) # 输出course_map,根据其内容选择要抢的课程所对应的编号 103 | lsltId_list = [71576210] 104 | fuck_course(lsltId_list, course_map, myheaders) # 开始抢课 105 | 106 | 107 | if __name__ == '__main__': 108 | main() 109 | -------------------------------------------------------------------------------- /登录获取cookies.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import re\n", 10 | "import json\n", 11 | "import time\n", 12 | "import hashlib\n", 13 | "import requests\n", 14 | "import warnings\n", 15 | "import pandas as pd\n", 16 | "from PIL import Image\n", 17 | "from io import BytesIO\n", 18 | "from IPython.display import display\n", 19 | "warnings.filterwarnings(\"ignore\")" 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": 2, 25 | "metadata": {}, 26 | "outputs": [], 27 | "source": [ 28 | "class VpnLogin(object):\n", 29 | " def __init__(self, vpn_name='vpns_username', vpn_passwd='vpns_passwd', uims_name='uims_username', uims_passwd='uims_passwd'):\n", 30 | " self.vpn_name = vpn_name # vpns的用户名\n", 31 | " self.vpn_passwd = vpn_passwd # vpns的密码\n", 32 | " self.uims_name = uims_name # uims的用户名\n", 33 | " self.uims_passwd = uims_passwd # uims的密码\n", 34 | " self.pattern1 = re.compile(r'用户名密码错误') # 判断是否登vpns成功的正则表达式\n", 35 | " self.pattern2 = re.compile(r'(.*)') # 判断是否登uims成功的正则表达式\n", 36 | " self._init_session()\n", 37 | " \n", 38 | " def _init_user_vpns(self):\n", 39 | " print('请输入vpns的用户名:', end='')\n", 40 | " self.vpn_name = input()\n", 41 | " print('请输入vpns的密码:', end='')\n", 42 | " self.vpn_passwd = input()\n", 43 | " \n", 44 | " def _init_user_uims(self):\n", 45 | " print('请输入uims的用户名:', end='')\n", 46 | " self.uims_name = input()\n", 47 | " print('请输入uims的密码:', end='')\n", 48 | " self.uims_passwd = input()\n", 49 | " \n", 50 | " def _init_session(self):\n", 51 | " session = requests.session()\n", 52 | " session.headers = {\n", 53 | " 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36', \n", 54 | " 'Content-Type': 'application/x-www-form-urlencoded', # 以form形式提交表单\n", 55 | " 'Connection': 'keep-alive',\n", 56 | " }\n", 57 | " self.session = session\n", 58 | " \n", 59 | " def _get_md5_passwd(self):\n", 60 | " # 通过分析js可得\n", 61 | " b_password = 'UIMS' + self.uims_name + self.uims_passwd\n", 62 | " md5_password = hashlib.md5(b_password.encode('UTF-8')).hexdigest()\n", 63 | " return md5_password\n", 64 | " \n", 65 | " def _login_vpns(self):\n", 66 | " # 获取wengine_vpn_ticket\n", 67 | " url1 = 'https://vpns.jlu.edu.cn/'\n", 68 | " self.session.post(url1, verify=False)\n", 69 | " # 登陆vpns.jlu.edu.cn\n", 70 | " url_2 = 'https://vpns.jlu.edu.cn/do-login' \n", 71 | " url2_data = 'auth_type=local&username=' + self.vpn_name + '&sms_code=&password=' + self.vpn_passwd\n", 72 | " verify_result = self.session.post(url_2, data=url2_data, verify=False)\n", 73 | " verify_res = re.findall(self.pattern1, verify_result.content.decode())\n", 74 | " return verify_res\n", 75 | " \n", 76 | " def _get_captcha(self):\n", 77 | " captcha_html = 'https://vpns.jlu.edu.cn/https/77726476706e69737468656265737421e5fe4c8f693a6445300d8db9d6562d/ntms/open/get-captcha-image.do?vpn-1&s=1'\n", 78 | " captcha_img = self.session.get(captcha_html, verify = False)\n", 79 | " # 保存验证码到本地并打开\n", 80 | " with open('./captcha.jpg', 'wb+') as f:\n", 81 | " f.write(captcha_img.content)\n", 82 | " f.close()\n", 83 | " image = Image.open('./captcha.jpg') # read image\n", 84 | " display(image)\n", 85 | " print('请输入验证码:', end='')\n", 86 | " vcode = input()\n", 87 | " return vcode\n", 88 | " \n", 89 | " def _login_uims(self):\n", 90 | " verify_html = 'https://vpns.jlu.edu.cn/https/77726476706e69737468656265737421e5fe4c8f693a6445300d8db9d6562d/ntms/j_spring_security_check'\n", 91 | " verify_dict = { \n", 92 | " 'username': self.uims_name,\n", 93 | " 'password': self._get_md5_passwd(), \n", 94 | " 'mousePath': '',\n", 95 | " 'vcode': self._get_captcha()\n", 96 | " }\n", 97 | " verify_result = self.session.post(verify_html, data = verify_dict, allow_redirects=True, verify=False)\n", 98 | " verify_res = re.findall(self.pattern2, verify_result.content.decode())\n", 99 | " return verify_res\n", 100 | " \n", 101 | " def login(self):\n", 102 | " # 登录vpns\n", 103 | " vpns_res = self._login_vpns()\n", 104 | " while len(vpns_res):\n", 105 | " print('vpns登录失败,原因:' + vpns_res[0])\n", 106 | " self._init_user_vpns() # vpns账号密码输入错误,重新输入\n", 107 | " self._init_session()\n", 108 | " vpns_res = self._login_vpns()\n", 109 | " print('vpns登录成功')\n", 110 | " # 登录uims\n", 111 | " uims_res = self._login_uims()\n", 112 | " while len(uims_res):\n", 113 | " print('uims登录失败,原因:' + uims_res[0])\n", 114 | " if(uims_res[0] == '登录错误:验证码无效或过期'):\n", 115 | " uims_res = self._login_uims() # 验证码无效或过期,只需要重新输入验证码\n", 116 | " else:\n", 117 | " self._init_user_uims() # uims账号密码错误,需要重新输入信息\n", 118 | " uims_res = self._login_uims() \n", 119 | " print('uims登录成功')\n", 120 | " \n", 121 | " \n", 122 | " def get_cookies(self):\n", 123 | " cookie = ''\n", 124 | " for x in self.session.cookies:\n", 125 | " cookie += x.name + '=' + x.value + ';'\n", 126 | " cookie = cookie[:-1]\n", 127 | " return cookie" 128 | ] 129 | }, 130 | { 131 | "cell_type": "code", 132 | "execution_count": 3, 133 | "metadata": {}, 134 | "outputs": [], 135 | "source": [ 136 | "vpn_login = VpnLogin('vpns的用户名', 'vpns的密码', 'uims的用户名', 'uims的密码')" 137 | ] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "execution_count": 4, 142 | "metadata": {}, 143 | "outputs": [ 144 | { 145 | "name": "stdout", 146 | "output_type": "stream", 147 | "text": [ 148 | "vpns登录成功\n" 149 | ] 150 | }, 151 | { 152 | "data": { 153 | "image/png": "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\n", 154 | "text/plain": [ 155 | "" 156 | ] 157 | }, 158 | "metadata": {}, 159 | "output_type": "display_data" 160 | }, 161 | { 162 | "name": "stdout", 163 | "output_type": "stream", 164 | "text": [ 165 | "请输入验证码:9018\n", 166 | "uims登录成功\n" 167 | ] 168 | } 169 | ], 170 | "source": [ 171 | "vpn_login.login()" 172 | ] 173 | }, 174 | { 175 | "cell_type": "code", 176 | "execution_count": 5, 177 | "metadata": {}, 178 | "outputs": [ 179 | { 180 | "name": "stdout", 181 | "output_type": "stream", 182 | "text": [ 183 | "refresh=1;wengine_vpn_ticket=94ebe2b4fe9d1b94\n" 184 | ] 185 | } 186 | ], 187 | "source": [ 188 | "cookie = vpn_login.get_cookies()\n", 189 | "print(cookie)" 190 | ] 191 | }, 192 | { 193 | "cell_type": "code", 194 | "execution_count": null, 195 | "metadata": {}, 196 | "outputs": [], 197 | "source": [] 198 | } 199 | ], 200 | "metadata": { 201 | "kernelspec": { 202 | "display_name": "Python 3", 203 | "language": "python", 204 | "name": "python3" 205 | }, 206 | "language_info": { 207 | "codemirror_mode": { 208 | "name": "ipython", 209 | "version": 3 210 | }, 211 | "file_extension": ".py", 212 | "mimetype": "text/x-python", 213 | "name": "python", 214 | "nbconvert_exporter": "python", 215 | "pygments_lexer": "ipython3", 216 | "version": "3.7.0" 217 | }, 218 | "toc": { 219 | "base_numbering": 1, 220 | "nav_menu": {}, 221 | "number_sections": true, 222 | "sideBar": true, 223 | "skip_h1_title": false, 224 | "title_cell": "Table of Contents", 225 | "title_sidebar": "Contents", 226 | "toc_cell": false, 227 | "toc_position": {}, 228 | "toc_section_display": true, 229 | "toc_window_display": false 230 | }, 231 | "varInspector": { 232 | "cols": { 233 | "lenName": 16, 234 | "lenType": 16, 235 | "lenVar": 40 236 | }, 237 | "kernels_config": { 238 | "python": { 239 | "delete_cmd_postfix": "", 240 | "delete_cmd_prefix": "del ", 241 | "library": "var_list.py", 242 | "varRefreshCmd": "print(var_dic_list())" 243 | }, 244 | "r": { 245 | "delete_cmd_postfix": ") ", 246 | "delete_cmd_prefix": "rm(", 247 | "library": "var_list.r", 248 | "varRefreshCmd": "cat(var_dic_list()) " 249 | } 250 | }, 251 | "types_to_exclude": [ 252 | "module", 253 | "function", 254 | "builtin_function_or_method", 255 | "instance", 256 | "_Feature" 257 | ], 258 | "window_display": false 259 | } 260 | }, 261 | "nbformat": 4, 262 | "nbformat_minor": 2 263 | } 264 | -------------------------------------------------------------------------------- /抢课爬虫.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 2, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import requests\n", 10 | "import warnings\n", 11 | "import json\n", 12 | "import time\n", 13 | "import pandas as pd\n", 14 | "warnings.filterwarnings(\"ignore\")" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": 3, 20 | "metadata": {}, 21 | "outputs": [], 22 | "source": [ 23 | "def get_headers(cookie):\n", 24 | " '''\n", 25 | " 根据cookies构建消息请求头\n", 26 | " ''' \n", 27 | " myheaders = {\n", 28 | " 'Content-Type': 'application/json',\n", 29 | " 'cookie': cookie,\n", 30 | " 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36',\n", 31 | " 'vpn-12-o2-uims.jlu.edu.cn':'',\n", 32 | " }\n", 33 | " return myheaders\n", 34 | "\n", 35 | "\n", 36 | "\n", 37 | "def get_course(myheaders):\n", 38 | " '''\n", 39 | " return:\n", 40 | " pd.DataFrame.from_dict(course_list): 已选以及未选的课程列表\n", 41 | " course_map: 未选的课程以及编号,该编号用于选课\n", 42 | " '''\n", 43 | " \n", 44 | " course_html = 'https://vpns.jlu.edu.cn/https/77726476706e69737468656265737421e5fe4c8f693a6445300d8db9d6562d/ntms/service/res.do?vpn-12-o2-uims.jlu.edu.cn'\n", 45 | " deHtml = 'https://vpns.jlu.edu.cn/https/77726476706e69737468656265737421e5fe4c8f693a6445300d8db9d6562d/ntms/service/res.do?vpn-12-o2-uims.jlu.edu.cn'\n", 46 | " course_dict = { \n", 47 | " 'tag': \"lessonSelectLog@selectStore\", \n", 48 | " 'branch': \"default\", \n", 49 | " 'params': {'splanId': 20} # 注意,此参数每学期不同会发生变化,不一定一直适用!!需要进行修改维护!!通过分析选课的请求可以得到!!\n", 50 | " }\n", 51 | " deInfo = {\n", 52 | " 'tag': \"lessonSelectLogTcm@selectGlobalStore\",\n", 53 | " 'branch': \"self\",\n", 54 | " 'params': {'lslId':0, 'myCampus':\"Y\"} # Y选课, N退课\n", 55 | " }\n", 56 | " type_dict = {3060:'必修课', 3061:'选修课', 3062:'限选课', 3063:'未知', 3064:'选修课', 3065:'校选修课'}\n", 57 | " course_list = []\n", 58 | " course_map = {}\n", 59 | " \n", 60 | " # 获取所有课程\n", 61 | " course_json = json.dumps(course_dict)\n", 62 | " course_result = requests.post(course_html, data = course_json, headers = myheaders, allow_redirects=True, verify=False)\n", 63 | " course_detail = json.loads(course_result.text)\n", 64 | " \n", 65 | " # 根据获取的json格式数据转存数据\n", 66 | " for i in range(len(course_detail['value'])):\n", 67 | " single = {}\n", 68 | " single['课程名称'] = course_detail['value'][i]['lesson']['courseInfo']['courName']\n", 69 | " single['选否'] = course_detail['value'][i]['selectResult']\n", 70 | " single['选课类型'] = type_dict[course_detail['value'][i]['applyPlanLesson']['selectType']]\n", 71 | " lslId = course_detail['value'][i]['lslId']\n", 72 | " deInfo['params']['lslId'] = lslId\n", 73 | " de_json = json.dumps(deInfo)\n", 74 | " de_result = requests.post(deHtml, data = de_json, headers = myheaders, allow_redirects=True, verify=False)\n", 75 | " de = json.loads(de_result.text)\n", 76 | " single['选课编号'] = de['value'][0]['lsltId']\n", 77 | " course_list.append(single)\n", 78 | " if course_detail['value'][i]['selectResult'] == 'N':\n", 79 | " course_map[single['选课编号']] = single['课程名称'] # 仅存储未选课程的编号,其他可以自行查看\n", 80 | " time.sleep(0.5) # 调速,防止被封\n", 81 | " return pd.DataFrame.from_dict(course_list), course_map\n", 82 | "\n", 83 | "\n", 84 | "\n", 85 | "def fuck_course(lsltId_list, course_map, myheaders):\n", 86 | " '''\n", 87 | " 根据lsltId_list中的课程id进行抢课\n", 88 | " '''\n", 89 | " \n", 90 | " course_detail = {\n", 91 | " 'lsltId': 0, \n", 92 | " 'opType': \"Y\" # Y选课, N退课\n", 93 | " }\n", 94 | " course_html = 'https://vpns.jlu.edu.cn/https/77726476706e69737468656265737421e5fe4c8f693a6445300d8db9d6562d/ntms/action/select/select-lesson.do?vpn-12-o2-uims.jlu.edu.cn'\n", 95 | " i = 1\n", 96 | " while True: # 抢课轮数,暂未设置所有课程抢课成功后终止,需要手动终止\n", 97 | " try:\n", 98 | " for lsltId in lsltId_list: # 抢每一门课\n", 99 | " course_detail['lsltId'] = lsltId\n", 100 | " detail_json = json.dumps(course_detail)\n", 101 | " result = requests.post(course_html, data = detail_json, headers = myheaders, allow_redirects=True, verify=False)\n", 102 | " info = json.loads(result.text) \n", 103 | " print(info)\n", 104 | " if info['count'] == 1:\n", 105 | " print('第' + str(i) + '次:'+ str(course_map[str(lsltId)]) +' 抢课成功') # 可用于捡漏\n", 106 | " else:\n", 107 | " print('第' + str(i) + '次:'+ str(course_map[str(lsltId)]) +' 抢课失败 原因:'+ info['msg'])\n", 108 | " #time.sleep(1) ###################### 调速 ######################\n", 109 | " i+=1\n", 110 | " except Exception as e: # 可能服务器突然没响应\n", 111 | " print(e)" 112 | ] 113 | }, 114 | { 115 | "cell_type": "code", 116 | "execution_count": 1, 117 | "metadata": {}, 118 | "outputs": [], 119 | "source": [ 120 | "# 下面为具体用法" 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "execution_count": 4, 126 | "metadata": {}, 127 | "outputs": [], 128 | "source": [ 129 | "cookie = 'wengine_vpn_ticket=................; refresh=1' # ...为你自己的cookie内容" 130 | ] 131 | }, 132 | { 133 | "cell_type": "code", 134 | "execution_count": 5, 135 | "metadata": {}, 136 | "outputs": [], 137 | "source": [ 138 | "myheaders = get_headers(cookie) # 获取请求头" 139 | ] 140 | }, 141 | { 142 | "cell_type": "code", 143 | "execution_count": 6, 144 | "metadata": {}, 145 | "outputs": [], 146 | "source": [ 147 | "df_course, course_map = get_course(myheaders) # 获取待抢课程编号" 148 | ] 149 | }, 150 | { 151 | "cell_type": "code", 152 | "execution_count": 7, 153 | "metadata": {}, 154 | "outputs": [ 155 | { 156 | "name": "stdout", 157 | "output_type": "stream", 158 | "text": [ 159 | "{'72243539': '托福与语言能力、策略培养', '72243874': '科学哲学概览', '72244134': '新媒体素养与传播', '72052829': '软件项目管理', '72052306': 'Windows程序设计', '71650363': '大数据技术与应用*', '71862761': '多媒体技术', '72137090': '线性规划', '71978271': '数据挖掘', '71701524': '生物信息学入门(双语)', '72134721': '虚拟现实技术基础', '71575961': '设计模式', '71846616': 'UML建模技术及应用', '71643243': '模糊数学与应用', '71566009': '数据库应用技术*', '71686416': '程序安全检测技术'}\n" 160 | ] 161 | } 162 | ], 163 | "source": [ 164 | "print(course_map) # 输出course_map,根据其内容选择要抢的课程所对应的编号" 165 | ] 166 | }, 167 | { 168 | "cell_type": "code", 169 | "execution_count": null, 170 | "metadata": { 171 | "scrolled": true 172 | }, 173 | "outputs": [ 174 | { 175 | "name": "stdout", 176 | "output_type": "stream", 177 | "text": [ 178 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 179 | "第1次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 180 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 181 | "第1次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 182 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 183 | "第2次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 184 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 185 | "第2次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 186 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 187 | "第3次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 188 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 189 | "第3次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 190 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 191 | "第4次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 192 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 193 | "第4次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 194 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 195 | "第5次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 196 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 197 | "第5次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 198 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 199 | "第6次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 200 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 201 | "第6次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 202 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 203 | "第7次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 204 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 205 | "第7次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 206 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 207 | "第8次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 208 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 209 | "第8次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 210 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 211 | "第9次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 212 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 213 | "第9次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 214 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 215 | "第10次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 216 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 217 | "第10次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 218 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 219 | "第11次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 220 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 221 | "第11次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 222 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 223 | "第12次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 224 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 225 | "第12次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 226 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 227 | "第13次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 228 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 229 | "第13次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 230 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 231 | "第14次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 232 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 233 | "第14次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 234 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 235 | "第15次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 236 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 237 | "第15次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 238 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 239 | "第16次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 240 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 241 | "第16次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 242 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 243 | "第17次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 244 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 245 | "第17次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 246 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 247 | "第18次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 248 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 249 | "第18次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 250 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 251 | "第19次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 252 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 253 | "第19次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 254 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 255 | "第20次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 256 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 257 | "第20次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 258 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 259 | "第21次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 260 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 261 | "第21次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 262 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 263 | "第22次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 264 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 265 | "第22次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 266 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 267 | "第23次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 268 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 269 | "第23次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 270 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 271 | "第24次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 272 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 273 | "第24次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 274 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 275 | "第25次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 276 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 277 | "第25次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 278 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 279 | "第26次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 280 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 281 | "第26次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 282 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 283 | "第27次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 284 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 285 | "第27次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 286 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 287 | "第28次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 288 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 289 | "第28次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 290 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 291 | "第29次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 292 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 293 | "第29次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 294 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 295 | "第30次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 296 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 297 | "第30次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 298 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 299 | "第31次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 300 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 301 | "第31次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 302 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 303 | "第32次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 304 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 305 | "第32次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 306 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 307 | "第33次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 308 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 309 | "第33次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 310 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 311 | "第34次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 312 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 313 | "第34次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 314 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 315 | "第35次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 316 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 317 | "第35次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 318 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 319 | "第36次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 320 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 321 | "第36次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 322 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 323 | "第37次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 324 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 325 | "第37次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 326 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 327 | "第38次:设计模式 抢课失败 原因:班级人数已经到达上限\n" 328 | ] 329 | }, 330 | { 331 | "name": "stdout", 332 | "output_type": "stream", 333 | "text": [ 334 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 335 | "第38次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 336 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 337 | "第39次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 338 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 339 | "第39次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 340 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 341 | "第40次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 342 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 343 | "第40次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 344 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 345 | "第41次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 346 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 347 | "第41次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 348 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 349 | "第42次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 350 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 351 | "第42次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 352 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 353 | "第43次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 354 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 355 | "第43次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 356 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 357 | "第44次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 358 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 359 | "第44次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 360 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 361 | "第45次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 362 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 363 | "第45次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 364 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 365 | "第46次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 366 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 367 | "第46次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 368 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 369 | "第47次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 370 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 371 | "第47次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 372 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 373 | "第48次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 374 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 375 | "第48次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 376 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 377 | "第49次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 378 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 379 | "第49次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 380 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 381 | "第50次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 382 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 383 | "第50次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 384 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 385 | "第51次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 386 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 387 | "第51次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 388 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 389 | "第52次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 390 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 391 | "第52次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 392 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 393 | "第53次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 394 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 395 | "第53次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 396 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 397 | "第54次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 398 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 399 | "第54次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 400 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 401 | "第55次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 402 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 403 | "第55次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 404 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 405 | "第56次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 406 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 407 | "第56次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 408 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 409 | "第57次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 410 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 411 | "第57次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 412 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 413 | "第58次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 414 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 415 | "第58次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 416 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 417 | "第59次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 418 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 419 | "第59次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 420 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 421 | "第60次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 422 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 423 | "第60次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 424 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 425 | "第61次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 426 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 427 | "第61次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 428 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 429 | "第62次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 430 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 431 | "第62次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 432 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 433 | "第63次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 434 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 435 | "第63次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 436 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 437 | "第64次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 438 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 439 | "第64次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 440 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 441 | "第65次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 442 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 443 | "第65次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 444 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 445 | "第66次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 446 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 447 | "第66次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 448 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 449 | "第67次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 450 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 451 | "第67次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 452 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 453 | "第68次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 454 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 455 | "第68次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 456 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 457 | "第69次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 458 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 459 | "第69次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 460 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 461 | "第70次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 462 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 463 | "第70次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 464 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 465 | "第71次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 466 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 467 | "第71次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 468 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 469 | "第72次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 470 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 471 | "第72次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 472 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 473 | "第73次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 474 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 475 | "第73次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 476 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 477 | "第74次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 478 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 479 | "第74次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 480 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 481 | "第75次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 482 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 483 | "第75次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 484 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 485 | "第76次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 486 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 487 | "第76次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 488 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 489 | "第77次:设计模式 抢课失败 原因:班级人数已经到达上限\n" 490 | ] 491 | }, 492 | { 493 | "name": "stdout", 494 | "output_type": "stream", 495 | "text": [ 496 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 497 | "第77次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 498 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 499 | "第78次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 500 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 501 | "第78次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 502 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 503 | "第79次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 504 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 505 | "第79次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 506 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 507 | "第80次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 508 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 509 | "第80次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 510 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 511 | "第81次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 512 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 513 | "第81次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 514 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 515 | "第82次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 516 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 517 | "第82次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 518 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 519 | "第83次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 520 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 521 | "第83次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 522 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 523 | "第84次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 524 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 525 | "第84次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 526 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 527 | "第85次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 528 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 529 | "第85次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 530 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 531 | "第86次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 532 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 533 | "第86次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 534 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 535 | "第87次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 536 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 537 | "第87次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 538 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 539 | "第88次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 540 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 541 | "第88次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 542 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 543 | "第89次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 544 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 545 | "第89次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 546 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 547 | "第90次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 548 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 549 | "第90次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 550 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 551 | "第91次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 552 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 553 | "第91次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 554 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 555 | "第92次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 556 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 557 | "第92次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 558 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 559 | "第93次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 560 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 561 | "第93次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 562 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 563 | "第94次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 564 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 565 | "第94次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 566 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 567 | "第95次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 568 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 569 | "第95次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 570 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 571 | "第96次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 572 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 573 | "第96次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 574 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 575 | "第97次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 576 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 577 | "第97次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 578 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 579 | "第98次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 580 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 581 | "第98次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 582 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 583 | "第99次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 584 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}\n", 585 | "第99次:数据挖掘 抢课失败 原因:上课时间冲突,不能选课\n", 586 | "{'count': 0, 'errno': 2080, 'items': None, 'msg': '班级人数已经到达上限', 'status': -12}\n", 587 | "第100次:设计模式 抢课失败 原因:班级人数已经到达上限\n", 588 | "{'count': 0, 'errno': 1995, 'items': None, 'msg': '上课时间冲突,不能选课', 'status': -6}" 589 | ] 590 | } 591 | ], 592 | "source": [ 593 | "lsltId_list = [71575961, 71978271]\n", 594 | "fuck_course(lsltId_list, course_map, myheaders) # 开始抢课" 595 | ] 596 | }, 597 | { 598 | "cell_type": "code", 599 | "execution_count": null, 600 | "metadata": { 601 | "scrolled": true 602 | }, 603 | "outputs": [], 604 | "source": [ 605 | "######\n", 606 | "######\n", 607 | "######\n", 608 | "# 测试用例以及部分错误,不必运行此块,仅供个人调试时参考\n", 609 | "######\n", 610 | "######\n", 611 | "######\n", 612 | "\n", 613 | "\n", 614 | "course_detail = {\n", 615 | " 'lsltId': \"70228928\", \n", 616 | " 'opType': \"N\" # Y选课, N退课\n", 617 | "}\n", 618 | "detail_json = json.dumps(course_detail)\n", 619 | "course_html = 'https://vpns.jlu.edu.cn/https/77726476706e69737468656265737421e5fe4c8f693a6445300d8db9d6562d/ntms/action/select/select-lesson.do?vpn-12-o2-uims.jlu.edu.cn'\n", 620 | "for i in range(1,3):\n", 621 | " try:\n", 622 | " result = requests.post(course_html, data = detail_json, headers = myheaders, allow_redirects=True, verify=False)\n", 623 | " info = json.loads(result.text)\n", 624 | " if info['count'] == 1:\n", 625 | " print('第' + str(i) + '次:抢课成功') # 可用于捡漏\n", 626 | " break\n", 627 | " else:\n", 628 | " print('第' + str(i) + '次:失败 原因:'+ info['msg'])\n", 629 | " except Exception as e: # 可能服务器突然没响应\n", 630 | " print(e)\n", 631 | " time.sleep(5) # 自动调速\n", 632 | "\n", 633 | " \n", 634 | "'''\n", 635 | "{\"count\":0,\"errno\":2080,\"items\":null,\"msg\":\"班级人数已经到达上限\",\"status\":-12}\n", 636 | "{\"count\":1,\"errno\":0,\"items\":[\"S\"],\"msg\":\"\",\"status\":0}\n", 637 | "{\"count\":0,\"errno\":1995,\"items\":null,\"msg\":\"上课时间冲突,不能选课\",\"status\":-6}\n", 638 | "{'count': 0, 'errno': -3, 'items': None, 'msg': '权限不足,您不能执行此操作或者获取数据', 'status': -3}\n", 639 | "{'count': 0, 'errno': 1931, 'items': None, 'msg': '尚未开始选课或者阶段不对', 'status': -12}\n", 640 | "\n", 641 | "{'applyPlanLesson': {'aplId': 524009,\n", 642 | " 'planDetail': {'credit': 2}, # 学分\n", 643 | " 'testForm': 3091,\n", 644 | " 'selectType': 3065, # 选课类型\n", 645 | " 'isReselect': 'N'}, # 选否\n", 646 | " 'notes': None, # 选课反馈\n", 647 | " 'selLssgCnt': 0,\n", 648 | " 'replyTag': None, # 反馈备注\n", 649 | " 'selectResult': 'N', # 选否\n", 650 | " 'lslId': 50245283,\n", 651 | " 'lesson': {'totalClassHour': 30,\n", 652 | " 'teachSchool': {'schoolName': '通信工程学院'},\n", 653 | " 'lessonId': 67787,\n", 654 | " 'courseInfo': {'courType2': 3040, 'courseId': 27121, 'courName': '医用机器人专题'}},\n", 655 | " 'sumLssgCnt': 1}\n", 656 | " ''' " 657 | ] 658 | } 659 | ], 660 | "metadata": { 661 | "kernelspec": { 662 | "display_name": "Python 3", 663 | "language": "python", 664 | "name": "python3" 665 | }, 666 | "language_info": { 667 | "codemirror_mode": { 668 | "name": "ipython", 669 | "version": 3 670 | }, 671 | "file_extension": ".py", 672 | "mimetype": "text/x-python", 673 | "name": "python", 674 | "nbconvert_exporter": "python", 675 | "pygments_lexer": "ipython3", 676 | "version": "3.7.0" 677 | }, 678 | "toc": { 679 | "base_numbering": 1, 680 | "nav_menu": {}, 681 | "number_sections": true, 682 | "sideBar": true, 683 | "skip_h1_title": false, 684 | "title_cell": "Table of Contents", 685 | "title_sidebar": "Contents", 686 | "toc_cell": false, 687 | "toc_position": {}, 688 | "toc_section_display": true, 689 | "toc_window_display": false 690 | }, 691 | "varInspector": { 692 | "cols": { 693 | "lenName": 16, 694 | "lenType": 16, 695 | "lenVar": 40 696 | }, 697 | "kernels_config": { 698 | "python": { 699 | "delete_cmd_postfix": "", 700 | "delete_cmd_prefix": "del ", 701 | "library": "var_list.py", 702 | "varRefreshCmd": "print(var_dic_list())" 703 | }, 704 | "r": { 705 | "delete_cmd_postfix": ") ", 706 | "delete_cmd_prefix": "rm(", 707 | "library": "var_list.r", 708 | "varRefreshCmd": "cat(var_dic_list()) " 709 | } 710 | }, 711 | "types_to_exclude": [ 712 | "module", 713 | "function", 714 | "builtin_function_or_method", 715 | "instance", 716 | "_Feature" 717 | ], 718 | "window_display": false 719 | } 720 | }, 721 | "nbformat": 4, 722 | "nbformat_minor": 2 723 | } 724 | --------------------------------------------------------------------------------