├── 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 |
--------------------------------------------------------------------------------