合工大抢课脚本


合肥工业大学教务系统选课分为三个阶段,第一个阶段不论人数,所有课程均可选择,由系统随机筛选中选学生(强烈吐槽!连续两年选课6门中1门,运气差到爆)。第二和第三阶段都是手动抢课,先到先得。
脚本在第二第三阶段有效,其原理就是不停监控课程已选学生数,一旦有空缺,就立即选课。而这空缺,一般是某些童鞋退课之后产生的空缺,或者相互交换课程时那一秒钟的空缺。

好了开始讲解令人激动的部分,脚本有两种形式,一种直接通过浏览器的控制台,使用JavaScript操作选课页面,不停刷新页面监控课程状态,不过这种方法运行久了之后比较吃内存,而且刷新速度一般(然而仍然比手动刷新快),但是操作简单,适合小白。另一种使用Python,资源消耗小,刷新速度快,但是有门槛。

JavaScript

var i=0;
function cir(){
    var course = document.querySelectorAll('[data-id="262589"]')[0];
    var status_course = document.querySelectorAll('[div="not-show-count\.text-danger"],[limit-count="300"]')[0];
    var closebtn = document.querySelectorAll('[data-dismiss="modal"],[class="btn btn-default close-modal"]')[2];

    if(status_course.innerText=="未满"){
        course.click();
        setTimeout(closebtn.click(),1500);
    }

    var next_page = document.querySelectorAll("a.page-link")[2];
    next_page.click();
    setTimeout(console.log(i++),1000);

    var last_page = document.querySelectorAll("a.page-link")[1];
    last_page.click();
}
setInterval(cir,2000);

详细解释:

  • 变量 i 用来记录监控次数,console.log在控制台输出,不然看不到运行状态. setInterval(cir,2000);即每2000ms运行一次函数cir().
  • 函数内部:document.querySelectorAll('[data-id="262589"]')[0];这是个选择器,筛选data-id为262589的元素,此处需更改id,更改方法下一段介绍. 该语句返回一个NodeList,包含一个元素–该课程对应的“选课”按钮。
  • status_course是该门课程的状态,选择div="not-show-count.text-danger",limit-count="300"属性的元素,即当前容量下的“已满”或者“未满”。

  • 点击选课按钮之后页面会出现弹窗,提示选课成功或者课程已满等等,弹窗有个关闭按钮,这就是closebtn,应该不需要更改(除非属性名称被改掉了,建议检查一下)。

  • if块的内部就是判断当前容量是已满还是未满,未满则点击“选课”按钮,并在1500ms之后点击弹窗上的关闭按钮setTimeout(closebtn.click(),1500);

  • 由于刷新页面会导致筛选课程里面填写的关键词消失,页面回到全校选课的首页,并且页面自动获取当前容量的信息间隔太长,因此刷新方法是点击页面底部的下一页,再点击上一页,完成刷新(确保使用合适的关键词,保证筛选到的课程有两页或以上).

  • var next_page = document.querySelectorAll("a.page-link")[2];这行就是筛选下一页的按钮,next_page.click();并点击。

  • setTimeout(console.log(i++),1000);这是在控制台输出,记录次数(运行时间过长吃内存或许是这个原因,可尝试在i为100或1000的整数倍时输出). 这里为什么要延时输出呢?其实我也不记得了。。

这样虽然看起来很麻烦,但是效率还可以,提高效率的方法是使用合适的关键词,使得该页的下一页也有需要抢的课程,这样就能在点击下一页之后,抢下一页的课程(这几乎不费时间)。

更改代码:

  1. course筛选时使用的data-id. 在需要抢的那门课的选课按钮上,右击,检查(Chrome)或者查看元素(Firefox)。元素可能会折叠,展开仔细查找data-id项,双击复制即可。
  2. status_course筛选时使用的div="not-show-count.text-danger", limit-count="300". 在未满或者已满文字上右击,检查,没记错的话,应该出现的就是div折叠项目,展开div,检查div属性值是否正确. 将div项下能展开的都展开,找到limit-count属性,双击复制值即可.
  3. closebtn同理. 随便点击一个选课按钮,在弹窗上右击,检查属性值
  4. next_page last_page 在页面底部,箭头图标上右击,检查属性。
  5. 延时以及循环间隔自己调整,循环低于1500,浏览器页面加载未完成,会导致点击失败,具体自己尝试.
  6. 控制台输入 document.querySelectorAll('[div="not-show-count.text-danger"],[limit-count="300"]')即使没有筛选到元素也会返回空列表,如果不好判断,使用 document.querySelectorAll('[div="not-show-count.text-danger"],[limit-count="300"]') [0],如果返回undefined,则表示没有筛选到元素,检查属性值是否正确.
  7. 关于status_course,很重要document.querySelectorAll('[div="not-show-count.text-danger"],[limit-count="300"]')[0];一般第0个元素就是目标课程,如果页面里含有多个limit-count="300"属性的元素,那么这句代码返回的是一个含有多个元素的列表,在控制台输入 document.querySelectorAll('[div="not-show-count.text-danger"],[limit-count="300"]'),(300更改为目标课程的课程人数限制)回车之后返回NodeList,展开将鼠标放到List下每个元素上时,选课页面对应的课程就会高亮显示,以此判断目标课程是第几个元素,据此修改[0]。类似这样:

Python

import requests
import time

headers = {
    'User-agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',
    'Host':'jxglstu.hfut.edu.cn'
    }
session = requests.Session()
session.verify = False

lessonId = ["299781"]
limit_count = 100
studentAssoc = "91160"
courseSelectTurnAssoc = "422"
virtualCost = "0"

cookies = {"SESSION": "79f3c2bb-84bf-46cc-8d0d-2232c8c25c2d", "SRVID": "s110"}
response = session.get("http://jxglstu.hfut.edu.cn/eams5-student/home", headers=headers, cookies=cookies)
print(response.status_code)

response = session.get("http://jxglstu.hfut.edu.cn/eams5-student/for-std/course-select/" + studentAssoc + "/turn/" + courseSelectTurnAssoc +"/select", headers=headers, cookies=cookies)
print(response.status_code)

i = 0
std_count = 0
while 1:
    i = i+1
    response = session.post("http://jxglstu.hfut.edu.cn/eams5-student/ws/for-std/course-select/std-count",
                            data={"lessonIds[]": lessonId}, headers=headers, cookies=cookies)
    # print(response.text)
    std_count = eval(response.text)[lessonId[0]]
    print(str(std_count) + '+' + str(i), end="\n" if i % 100 == 0 else "  ")
    if std_count <= limit_count:
        data = {'studentAssoc': "91160", 'lessonAssoc': lessonId[0], 'courseSelectTurnAssoc': courseSelectTurnAssoc,
                'scheduleGroupAssoc': '', 'virtualCost': virtualCost}
        response = session.post("http://jxglstu.hfut.edu.cn/eams5-student/ws/for-std/course-select/add-request",
                                headers=headers, data=data, cookies=cookies)
        print(response.text, end="\n\n\n\n\n\n")
    time.sleep(0.5)

不详解,会的自然懂,不会的,看看requests库。讲几个注意事项:

  1. 流程:使用cookie登录信息门户,发送请求获取对应课程id的已选课学生数std_count,如果小于课程人数限制limit_count,即可发送选课请求,完成抢课.

  2. lessonId需更改,即JavaScript部分中course的data-id属性值,limit_count即status_course的limit-count属性值.

  3. studentAssoc是每个学生对应的序号,查看方法:打开信息门户的本科教务页面,点击任一项,比如“课表”,查看网址栏,最后的几位数字91160就是studentAssoc

  4. courseSelectTurnAssoc这是选课的轮次,选课未开放,无法查看。可在选课开放之后,使用Fiddle抓取点击选课按钮之后发送的数据包,webform一栏有 courseSelectTurnAssoc的值.

  5. virtualCost似乎没什么用,不用更改.

  6. cookies字典下的两个键值修改. 登录选课页面,右击,检查,Application,Cookies,找到Domain为 jxglstu.hfut.edu.cn的两个cookie(或更多),复制Value,替换代码中的值即可.

  7. 同时抢多门课. 添加课程代码到lessonId列表中,并在while循环内添加判断. 自行调试.

  8. 延时建议0.5s以上,太快会导致服务器返回错误数据.

结尾
本教程仅供学习使用,作者不承担使用者因滥用造成的一切后果. 谨慎使用


文章作者: Tqraf
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Tqraf !
评论
  目录