实现某方正教务系统抢课
本帖最后由 血海修罗 于 2023-12-30 11:30 编辑某方正教务系统抢课
前言今年年初学习了shiyier大佬的帖子:女朋友教务系统抢课老抢不上,写个代码解决这个问题,搞定了登陆的问题,但由于大佬没有给出抢课的代码,身为苦逼大学生的我在暑假简单学习了下python和爬虫,自己实现了抢课的功能,并在近几天经过了实测,赶在今年结束前与大家分享一下思路。
功能实现
1.实现登录 具体思路请参考下帖:
女朋友教务系统抢课老抢不上,写个代码解决这个问题
https://www.52pojie.cn/thread-1739086-1-1.html
(出处: 吾爱破解论坛)
2.实现选课
即本帖内容
分析网页
选课嘛,我们先随便选一门看看有什么东西
很明显的可以看到浏览器发送了两个请求,那我们就看看这俩分别有哪些参数
可以看到无论是第一个请求还是第二个请求都有jxb_ids、kch_id、zyh_id这三个参数,很明显这仨很重要,那就想方设法的获取一下这仨参数
获取zyh_id和xkkz_id
不管这几个参数多复杂,我们先去网页源代码看看,万一真的简单的放在了源代码里呢?
经过楼主的一通操作,发现源代码里的确有存放其中的几个参数,那就是zyh_id和xkkz_id,很明显zyh_id和xkkz_id就是代表所选的课程分属的版块,那这个简单的爬一下源代码就可以了
下面是代码,里面的yhm是你的学号
#获取zyh_id
def get_zyh_id(yhm):
zyh_id_url = 'http://***.**.***.***/jwglxt/xsxk/zzxkyzb_cxZzxkYzbIndex.html?gnmkdm=N253512&layout=default&su=' + str(yhm)
response = session.get(url=zyh_id_url)
response.encoding = 'utf-8'
content = response.text
tree = etree.HTML(content)
zyh_id = tree.xpath('//input[@id="zyh_id"]/@value')
return zyh_id
#爬取xkkz_id
def course_sort(yhm):
sort_url = 'http://***.**.***.***/jwglxt/xsxk/zzxkyzb_cxZzxkYzbIndex.html?gnmkdm=N253512&layout=default&su=' + str(yhm)
response = session.get(url=sort_url,headers=headers1)
response.encoding = 'utf-8'
content = response.text
tree = etree.HTML(content)
course_sort_id = tree.xpath('//a[@role="tab"]/@onclick')
return course_sort_id
#因为xkkz_id不是每次都会发生变化,所以这里把xkkz_id写入到文件里,不用每次都爬取
def get_course_sort_id_true(list):
pattern = r"queryCourse\(this,'(?:[^']*)','(\w+)'"
extracted_texts = []
for text in list:
match = re.search(pattern, text)
if match:
extracted_text = match.group(1)
extracted_texts.append(extracted_text)
with open("courese_sort_id.txt", "w") as file:
for item in extracted_texts:
file.write(item + '\n')
获取jxb_ids和kch_id
这俩在网页源代码里没有,那么就先搜一下
于是就找到了这串代码
很明显基本上所有的参数都是在这个函数里生成的,但是楼主水平不够,不知道怎么通过这个函数获取这些参数,所以采用了一个比较迂回的办法,有兴趣的大佬可以看看怎么从这里获得参数。
下面是楼主的思路:
我们知道选课可以搜索,搜索就必然要返回给我们数据,我们还知道,每门课程都有一个与其唯一对应的教学班编号,那我们就搜一下呗
如图所示,搜索之后给了我们两条数据,打开一看,里面就正好有我们想要的jxb_ids和kch_id
那知道这俩参去哪获取了,同样的去爬一下就可以了,不同的是这里的参数是通过搜索获得的,所以需要通过两个post请求获得
#获取kch_id
def get_data_kch_id(zyh_id,yhm):
serch_url = 'http://***.**.***.***/jwglxt/xsxk/zzxkyzb_cxZzxkYzbPartDisplay.html?gnmkdm=N253512&su=' + str(yhm)
data = {
#这里的filter_list是你搜索的教学班编号,下同
'filter_list':filter_list,
'rwlx':rwlx,
'xkly':'0',
'bklx_id':bklx_id,
'sfkkjyxdxnxq':'0',
'xqh_id':'05',
'jg_id':'07',
'njdm_id_1':'2022',
'zyh_id_1':str(zyh_id),
'zyh_id':str(zyh_id),
'zyfx_id':'wfx',
'njdm_id':'2022',
'bh_id':'20220809113',
'bjgkczxbbjwcx':'0',
'xbm':'1',
'xslbdm':'wlb',
'mzm':'01',
'xz':'4',
'ccdm':'3',
'xsbj':'4294967296',
'sfkknj':'0',
'sfkkzy':'0',
'kzybkxy':'0',
'sfznkx':'0',
'zdkxms':'0',
'sfkxq':'0',
'sfkcfx':'0',
'kkbk':'0',
'kkbkdj':'0',
'sfkgbcx':'0',
'sfrxtgkcxd':'0',
'tykczgxdcs':'0',
'xkxnm':'2023',
'xkxqm':'12',
'kklxdm':kklxdm,
'bbhzxjxb':'0',
'rlkz':'0',
'xkzgbj':'0',
'kspage':'1',
'jspage':'10',
'jxbzb':''
}
response = session.post(url=serch_url,data=data,headers=headers1)
kch_id_1 = response.json()
with open('kch_id_1.json', 'w', encoding='utf-8') as fp:
fp.write(str(kch_id_1))
# 取课程jxb_ids
def get_jxb_ids(yhm,kch_id):
choice_url = 'http://***.**.***.***/jwglxt/xsxk/zzxkyzbjk_cxJxbWithKchZzxkYzb.html?gnmkdm=N253512&su=' + str(yhm)
data_beggin = {
'filter_list':filter_list,
'rwlx':rwlx,
'xkly':'0',
'bklx_id':bklx_id,
'sfkkjyxdxnxq':'0',
'xqh_id':'05',
'jg_id':'07',
'zyh_id':str(zyh_id),
'zyfx_id':'wfx',
'njdm_id':'2022',
'bh_id':'20220809113',
'xbm':'1',
'xslbdm':'wlb',
'mzm':'01',
'xz':'4',
'ccdm':'3',
'xsbj':'4294967296',
'sfkknj':'0',
'sfkkzy':'0',
'kzybkxy':'0',
'sfznkx':'0',
'zdkxms':'0',
'sfkxq':'0',
'sfkcfx':'0',
'bbhzxjxb':'0',
'kkbk':'0',
'kkbkdj':'0',
'xkxnm':'2023',
'xkxqm':'12',
'xkxskcgskg':'1',
'rlkz':'0',
'kklxdm':kklxdm,
'kch_id': str(kch_id),
'jxbzcxskg':'0',
'xkkz_id': xkkz_id,
'cxbj':'0',
'fxbj':'0'
}
response = session.post(url=choice_url,data=data_beggin,headers=headers1)
course = response.json()
with open('course.json', 'w', encoding='utf-8') as fp:
fp.write(str(course))
with open('course.json', 'r', encoding='utf8') as f:
jxb_id = f.read()
jxb_id = jxb_id.replace("'", '"')
jxb_id = jxb_id.replace("False", '"False"')
jxb_id = jxb_id.replace("True", '"True"')
jxb_id_data = json.loads(jxb_id)
jxb_id_true = jsonpath.jsonpath(jxb_id_data, '$..do_jxb_id')
return jxb_id_true
完成选课
其他的的参数就无伤大雅了,例如xkxnm代表的是学年,xkxqm代表的是学期,以我们学校为例,今年是2023学年,03是上学期,12是下学期,选课前稍微修改一下就可以了
那么我们回到分析网页的第一张截图
选课时发了两个请求,那我们也发送两个请求,实现代码如下
#发送第一次请求
def choice_course_1(yhm,jxb_ids,kch_id,zyh_id):
course_url_1 = 'http://***.**.***.***/jwglxt/xsxk/zzxkyzb_cxXkTitleMsg.html?gnmkdm=N253512&su=' + str(yhm)
data = {
'jxb_ids': str(jxb_ids),
'xkxnm': '2023',
'xkxqm': '12',
'bj': '7',
'kch_id': str(kch_id),
'njdm_id': '2022',
'zyh_id': str(zyh_id),
'kklxdm': kklxdm
}
response = session.post(url=course_url_1,data=data,headers=headers1)
course = response.json()
with open('choice_course_1.json', 'w', encoding='utf-8') as fp:
fp.write(str(course))
#这里写入course是为了判断是否选课成功
#发送第二次请求
def choice_course_2(yhm,jxb_ids,kch_id,zyh_id):
course_url_2 = 'http://***.**.***.***/jwglxt/xsxk/zzxkyzbjk_xkBcZyZzxkYzb.html?gnmkdm=N253512&su=' + str(yhm)
data = {
'jxb_ids': str(jxb_ids),
'kch_id': str(kch_id),
'rwlx': rwlx,
'rlkz': '0',
'rlzlkz': '1',
'sxbj': '1',
'xxkbj': '0',
'qz': '0',
'cxbj': '0',
'xkkz_id': xkkz_id,
'njdm_id': '2022',
'zyh_id': str(zyh_id),
'kklxdm': kklxdm,
'xklc': '1',
'xkxnm': '2023',
'xkxqm': '12'
}
response = session.post(url=course_url_2,data=data,headers=headers1)
course = response.json()
with open('choice_course_2.json', 'w', encoding='utf-8') as fp:
fp.write(str(course))
#这里写入course是为了判断是否选课成功
写完了简单运行下
嗯还是可以的,至于这个图里的内容有个编号,因为我们获取参数的时候是通过搜索教学班编号完成的所以在通过代码选课的时候仍然需要这个教学班编号,这个每个学校选课前应该会发个文件,里面就有。
结语
对于不同的学校选课时的参数可能会有细微的差别,各位同学可以对比着自己修改一下
因为楼主是小白,只是简单的学习了下爬虫和python,所以思路可能有些混乱,代码可能有很多问题,希望各位大佬不吝赐教
代码
以下是原代码
这个代码是楼主前两天选课的时候用的,只是把域名和学号密码用*替换了,大家可能要自己改一下才能用
里面的yhm是学号,mm是密码。 抢课已经有了,有没有无痕改期末分数
非常感谢 谢谢分享 非常感谢的
非常感谢 谢谢分享 vocal 我们就是这个系统,虽然我已经修完了选修但是还是感谢楼主分享 ### 之前分析我们学校的抢课系统,也写过油猴插件和软件,github上也有一个API文档写了一些分析。参数全都在js的异步请求上写着的,可以去分析一下。
### 最后的发包参数如下:
| 变量名 | 值(示例) | 获取途径 | 注释 | 是否必须 |
| --------- | ------------------------------------- | --------------------------------------------- | ----------------------------------------------------- | --------------- |
| `jxb_ids` | 7cad8a50a08b2c9...(共257个字符) | [具体信息]`do_jxb_id` | 教学班号(动态刷新) | ✓ |
| `kch_id`| 45C16EB07F0B1AC3E0530264A8C024C7 | [初级搜索]`kch_id` | 课程号 | ✓ |
| `qz` | 0 | ----- | 权重 | ✓ |
| `kklxdm`| 10 | [主页面] | `10`为选修课` 01`为主修课 | 主修:? 选修:X |
| `zyh_id`| A4F6C96AD6254D5AE05315C6A8C0XXXX | [主页面] | 专业代码(若选课对专业有要求则需要,大部分主修课有要求) | 主修:? 选修:X |
| `kcmc` | (66172392)党政机关公文写作 - 1.0 学分 | ----- | 课程名称(与[初级搜索]返回数据有些许差别) | 主修:? 选修:X |
| `rwlx` | 2 | [主页面] | | 主修:? 选修:X |
| `rlkz` | 0 | [主页面] | | 主修:? 选修:X |
| `rlzlkz`| 1 | [主页面] | | 主修:? 选修:X |
| `sxbj` | 1 | 如果`rlkz`或`rlzlkz`为1,则为1,否者为0 | | 主修:? 选修:X |
| `xxkbj` | 0 | [初级搜索] | | 主修:? 选修:X |
| `cxbj` | 0 | [已选课程] [初级搜索] | 重修标记 | 主修:? 选修:X |
| `xkkz_id` | 0CECD6B11EFF2A6BE0630BC6A8C0D106 | [主页面]`firstXkkzId`因为`xkkz_id`为空 | 与年级,主修课还是选修课相对应 | 主修:? 选修:X |
| `xklc` | 1 | [主页面] | | 主修:? 选修:X |
| `xkxnm` | 2023 | [主页面] | 当前学期年份如2023-2024 即2023 | 主修:? 选修:X |
| `xkxqm` | 12 | [主页面] | | 主修:? 选修:X | 还有,jxb_ids是后端动态生成的,有一定的时效性 戮之使 发表于 2024-2-18 16:14
### 之前分析我们学校的抢课系统,也写过油猴插件和软件,github上也有一个API文档写了一些分析。参数 ...
js还在学...... 等学完了再研究研究 hua111 发表于 2024-2-28 08:58
有一定的时效性
总的思路是差不多的 可能过段时间其中的参数会发生变化 要相应地改一下
页:
[1]
2