目录
(一)核心功能:模拟登陆
(二)使用Cookie进行模拟登录
(三)获取教务网选课列表
(四)循环选课
(五)断线重连
Github链接: https://github.com/njuwuyuxin/CourseGrabber
循环选课
在成功实现了登陆系统,拉取课程列表之后,我们离成功只差最后一步,只需要模拟浏览器,向对应端口发送选课请求即可。
手动在网页上选择任意一门课之后发现,选课请求体结构非常简单,同样为Post请求
{
'method':'addSpecialitySelect',
'classId':'xxxx'
}
这里的classID就是上一篇中提到的后台为每个课程标记的ID,并不是大家平时使用的课程号。好在上一篇中,我们已经对每门课的序号和课程ID进行了映射。
基本思路理清后,代码的部分就相对非常简单。
def GrabCourse(courseID,interval=0):
while(True):
selectCourse_reqdata={}
selectCourse_reqdata['method']="addSpecialitySelect"
selectCourse_reqdata['classId']=str(courseID)
selectResult = s.post(host+'student/elective/selectCourse.do',selectCourse_reqdata)
soup = BeautifulSoup(selectResult.content,"html.parser",from_encoding='utf-8')
for tag in soup.find_all('div'):
if tag.get('id')=="successMsg":
print("抢课成功!")
return
elif tag.get('id')=="errMsg":
if tag.string.find("已经")!=-1:
print("您已经抢到该课程啦~")
exit()
elif tag.string.find("错误")!=-1:
print("出现错误,添加失败")
exit()
else:
print("当前班级已满,仍在为您持续抢课")
else:
pass
if interval!=0:
time.sleep(interval)
这里的GrabCourse函数接收两个参数,第一个就是课程ID,第二个为一个可调的时间间隔。为了避免对教务系统造成过大负担(防止被查水表),这里默认设置了每次发送选课请求的时间间隔为1秒。
同时对每次选课请求的返回进行一下检验,主要分为四种情况:
- 选课成功:理想情况
- 已经选课:证明课表这已经选中这门课
- 班级已满:抢课系统主要针对的正是这种情况,班级满时需要循环发送请求,等待班级空出位置的瞬间。
- 出现错误:多为课程ID填写错误,或者选择了其他院系专业课(没有选课权限)等情况
对每种情况分别处理即可
其他尝试
由于之前拉取课程列表时,尝试通过填写其他院系编号来构造请求体,成功拉取到了其他院系的课表,可知教务系统后端对院系方面审核并不十分严格。因此在选课阶段同样进行了类似的尝试(作死),方法同样是在构造选课请求时,填写其他院系课程的课程ID
结果:返回“出现错误,添加失败”(笑)
可见教务平台至少在选课的时候还是稍微做了一下身份验证。不过至此,整个抢课系统的基本功能已经实现。可以成功登录、获取列表、循环发送选课请求。接下来的工作就是优化人机交互以及断线重连相关功能。
网友评论