免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

抢课脚本怎么做?从登录到自动提交的完整技术拆解

抢课脚本怎么做?从登录到自动提交的完整技术拆解 简介面向北京邮电大学本科生的自动抢课工具基于JavaScript实现针对教务系统登录后的选课流程设计无需进入选课界面即可按课程名自动匹配并抢课覆盖必修、选修与公选课。压缩包共2个文件包含1个JavaScript脚本和1个Markdown说明文档整体仅2KB轻量简洁。脚本支持通过courses变量自定义目标课程通过interval调整轮询间隔抢课模式可设50ms捡漏模式建议300ms兼顾不同场景下的成功率与服务器压力。已有2579人学习下载适合熟悉基本浏览器调试操作、希望提升选课效率的在校学生。除核心脚本外说明文档还梳理了启动时机、异常停止、与手动抢课配合等实用经验同时提醒勿设置过低间隔、勿恶意抢课引导合理使用。 每年选课季北邮的本科生群都会哀嚎一片。热门通识课、体育课开选即满刷新、提交、失败、再刷新这套循环能磨掉人半条命。我写了个叫 BUPTtakeCourse 的小工具输入课程名它自动完成登录、检索、提交选课请求到点自动开抢。这篇博文把我做这个抢课脚本的完整思路、技术拆解和踩坑记录整理出来希望能帮到同样有选课需求的学弟学妹也适合想了解校园自动化脚本怎么写的人参考。1. 项目背景与核心需求拆解1.1 选课季到底难在哪北邮的选课系统在高峰期页面卡顿、接口超时是常态。名额有限的课程比如热门公选课、体育专项课往往开放选课的第一分钟就被秒空。手动操作的流程看起来简单登录、搜索课程、点选课、确认提交。但真正经历过的人都知道问题出在高频和快速响应的矛盾上——你还在等页面刷新别人已经提交完了。抢课的本质其实是把“查询余量”和“提交选课”这两个动作反复执行直到成功。这个场景天然适合脚本化流程固定、频次高、对响应速度要求苛刻。人工操作有两大痛点一是手速跟不上节奏二是老得盯屏幕一盯就是半小时起步。脚本的价值不是作弊而是把这些重复劳动交给程序让它在后台持续监控一旦发现余量就第一时间提交。1.2 “按课程名自动选课”的真实需求这个项目最关键的设计点名字里已经写清楚了根据课程名自动选课。很多人会问为什么不直接用课程代码因为对普通学生来说课程代码记不住但“电路分析基础”“羽毛球初级”这类课程名是一眼就能认出来的。用课程名做主输入需要额外解决两个问题。第一是课程名可能重复不同老师开课、不同时段开课课程名一样第二是系统里的课程名和选课界面显示的名称可能不完全一致比如多了“A班”这种后缀。所以脚本的核心逻辑不是简单地拿字符串去比较而是要有一套匹配策略把课程名、上课时间、任课教师等信息组合起来先识别、再确认最后才发起选课。1.3 脚本的能力边界需要先把定位说清楚BUPTtakeCourse 不是漏洞利用工具也不是绕过认证的违规程序。它的前提是学校选课系统本身允许你选这门课你有选这门课的身份资格只是名额紧张、需要拼手速。脚本做的事情和人工抢课时一直在点“提交”按钮没有本质区别只是把它变成了自动化的高频检测一旦余量出现就立刻提交。这个边界很重要。我在设计脚本时就定了几个原则不碰验证码破解、不尝试绕过登录认证、不滥用高频请求轮询频率控制在合理范围内避免对教务系统造成额外压力。脚本只是把你的个人操作变快而不是破坏规则。2. 整体方案设计与技术选型2.1 自动化方案的选型逻辑做这类校园自动化脚本有两套主流方案模拟 HTTP 请求和浏览器自动化。模拟请求就是用 requests 库直接构造登录、查询、选课的请求优点是轻量、快、资源占用小能在极小延迟内完成整套操作缺点是必须先分析出系统的接口规律还要处理登录态、参数加密、可能的验证码。浏览器自动化比如 Selenium 或 Playwright则是启动一个真实浏览器模拟人的点击行为。它的优点是几乎不用逆向接口页面怎么操作代码就怎么写缺点是速度慢每次启动浏览器都要几秒钟内存开销也大在抢课这种拼毫秒的场景下反而吃亏。BUPTtakeCourse 选用的是以请求模拟为主、浏览器自动化兜底的混合策略。主流程全部走 HTTP 请求这样速度快遇到个别必须要验证码或复杂前端逻辑的场景再临时唤起浏览器人工介入处理一次拿到登录态后继续走请求流程。2.2 技术栈与依赖选择整个项目基于 Python 3依赖库非常克制核心就几个requests负责所有 HTTP 请求网络交互的基础PyYAML读取配置文件把课程名、轮询间隔、账号信息等参数外置BeautifulSoup4 lxml解析选课页面返回的 HTML 表格logging本地日志记录方便排查问题smtplib / requests结果通知邮件或微信推送渠道二选一选这些库的考虑是稳定、成熟、没有太深的坑。requests 是 Python 社区的基础库各种加密参数虽然要手动处理但网上案例多BeautifulSoup 做 HTML 表格解析足够用不需要引重型爬虫框架。后续如果接口返回的不是 HTML 而是 JSON解析模块可以直接替换成 json.loads改动成本很小。2.3 目录结构与模块划分项目拆成几个职责单一的模块后期维护起来非常舒服BUPTtakeCourse/ ├── main.py # 入口调度逻辑 ├── config.yaml # 配置文件账号与选课目标 ├── login.py # 统一身份认证登录 ├── course.py # 课程查询与目标匹配 ├── selector.py # 选课请求提交 ├── monitor.py # 轮询监控与状态机 ├── notifier.py # 结果通知 └── logger.py # 日志初始化模块划分的原则是登录只管登录查询只管查询选课只管选课。尤其是登录态单独放一个模块通过全局 session 对象共享其他模块不关心 cookie 怎么来的只负责在请求时带上它。这样有个好处后面如果要给脚本加多账号支持只需要改动 login.py 的调用方式其他模块不用动。3. 核心模块实现与细节解析3.1 统一身份认证登录模块登录是整套脚本的地基。北邮的账号体系走的是统一身份认证登录状态拿到手之后后续的课程查询、选课提交都依赖这个 session 里的 cookie。我实现登录模块时遇到了最常见的三个坑。第一个是登录请求中往往带有动态 token 或隐藏字段需要先从登录页抓取再拼进 POST 表单第二个是密码传输经常不是明文有的系统用 RSA 公钥加密公钥要从某个接口动态获取第三个是部分环境会弹验证码这种情况自动识别成本太高我选择了折衷方案——检测到验证码时脚本暂停并向终端输出提示人工输入一次验证码后继续。登录成功后把 session 的 cookie 序列化保存到本地文件。这样脚本重启之后如果 cookie 还在有效期内可以直接复用不需要每次都走登录流程。选课系统卡顿的时候重复登录本身也是一种负担。3.2 课程查询与目标匹配课程查询接口通常会返回当前学期所有课程列表或者按关键字匹配的课程子集。页面一般是一张表格里面包含课程名、课程号、任课教师、上课时间、剩余名额这些信息。course.py 负责把这张表解析成结构化数据然后与 config.yaml 里配置的目标课程做匹配。匹配逻辑我做了三层校验避免误选同名课程。第一层是课程名包含匹配比如配置的是“羽毛球”那“羽毛球初级班”“羽毛球提高班”都能命中第二层是校区或时段校验如果你只想选周六上午的课脚本会过滤掉其他时段的选项第三层是人工确认机制如果匹配到的课程多于一条脚本会先把结果打印出来让用户选一条避免脚本自作主张。这一层最关键的是要拿到稳定字段。有些系统的课程名单里课程名和选课提交时的参数不是一回事课程名只是给人看的真正提交时用的是课程号或开课编号。所以 course.py 在匹配到课程后必须把该课程对应的提交参数也一起提取出来存成一条“选课目标”。3.3 自动选课与重试策略selector.py 负责真正提交选课请求。提交接口接收课程标识、学期等参数一般返回一个状态码显示选课成功、课容量已满、时间冲突或者未到选课时间。这些状态码必须逐一映射成可读的中文提示方便判断失败原因。重试策略是个值得细讲的地方。这里不能直接用 while True 死循环高频率提交而是要做状态机状态一等待开始选课时间未到低频轮询每个 30 秒查一次状态二监控余量时间已到每 3 到 5 秒查一次余量状态三提交选课余量大于 0立即提交状态四成功结束收到成功返回后停止该课程的监控这样设计的好处是不需要全程高频轰炸对系统友好也不容易被风控。每次请求之间我加了随机延迟不是固定间隔而是 2 到 5 秒内的随机值这样请求节奏更接近人的行为也避免了所有请求在同一时刻发出造成的瞬时拥塞。3.4 定时触发与结果通知选课不是全天开放的系统会在某个时刻统一放名额。monitor.py 里我通过比较本地时间与配置的选课开始时间来切换状态时间没到就处于等待状态时间一到自动进入监控状态。这里要注意服务器时间可能和本地时间不一致我建议第一次运行时先从教务系统响应头里拿一次服务器时间以它为准。通知模块用的方式比较轻成功后通过邮件或 Server酱 推一条消息到微信。我实际用下来推送比看日志靠谱得多选课成功的那一刻你未必在电脑前但手机通知一定不会错过。失败次数太多时也会推送一条提示方便及时调整策略。4. 完整实操流程与运行调优4.1 环境准备与项目启动先用 Python 3.8 以上版本跑通依赖创建虚拟环境后安装这几个包pip install requests pyyaml beautifulsoup4 lxml在 config.yaml 里填好学号和密码配置目标课程信息account: username: 你的学号 password: 你的密码 targets: - name: 羽毛球初级 campus: 本部 time: 周六上午 schedule: start_time: 2025-02-20 10:00:00 interval_wait: 30 interval_query: 3 notify: email_enabled: false serverchan_key: 第一次运行建议只测试登录流程不开启选课监控。看到日志输出登录成功、课程列表解析正常之后再带入真实选课时间去跑完整流程。4.2 轮询间隔与并发参数调优轮询间隔直接决定脚本的抢课成功率也决定了脚本给系统带来的压力。我实测下来interval_query 设为 3 秒比较合适能保证在名额释放后的几秒内发起提交又不至于因为请求太密集导致自己的 IP 被临时限制。设置成 1 秒以内反而容易触发风控。提交选课时使用了线程池限制并发数默认只支持一个目标课程同时提交。因为多门课同时提交会引发冲突校验比如同一时间段的课同时选系统会判定冲突。如果确实想同时抢多门不同时间的课每个课程跑一个独立监控实例并且每个请求之间保持 1 秒以上的间隔优先级从上到下排列。4.3 日志观测与成功判定运行过程中最重要的是看日志。我在每个关键环节都加了日志登录成功、课程匹配到几条、进入监控状态、检测到余量、提交结果、成功通知。日志格式统一为时间、级别、模块、内容四段方便 grep2025-02-20 10:00:03,234 INFO couse.py 目标课程羽毛球初级匹配到2条记录 2025-02-20 10:00:06,531 INFO selector.py 第1次提交,返回码200,选课成功成功判定必须看系统返回的状态码而不是只看 HTTP 200。有些系统 HTTP 返回 200 但内容里写着“选课失败时间冲突”。我把常见结果码整理成映射表遇到未定义的状态码直接输出原始报文方便后续补充。5. 常见问题与排查技巧实录5.1 登录状态失效导致后续全部报错最常见的问题是脚本跑着跑着突然连续报“未登录”或“无权限”。原因是统一身份认证的 session 过期或者因为多次请求被系统踢下线。排查时先看日志里是不是登录模块才有的错误确认后就重新登录。我的处理办法是在 monitor.py 里做一个响应状态拦截器所有接口返回“未登录”状态码时自动触发一次重新登录然后继续任务。这样 session 过期对用户来说几乎无感知只要账号没掉线就不会中断监控。5.2 请求被临时限制频率设置过高时接口会返回请求频率超限或直接返回验证码页面。这个问题的本质是请求模式太像机器了。我调整过几个参数后效果很明显轮询间隔从固定的 2 秒改成 2 到 5 秒之间的随机值每次提交选课请求之前额外 sleep 0.3 到 0.8 秒去掉了多线程并发查询同一个接口的逻辑。真实心塞的是有段时间我把并发调得过高IP 被限制了好几个小时正好错过了当天名额。后来学乖了抢课脚本不能贪快稳定比快更重要。5.3 课程名匹配到多条记录导致选错课课程名匹配得太宽泛可能会出现“命中了 5 条记录、脚本默认选了第一条结果”的情况。我踩过这个坑差点选错课。解决方式前面提到过匹配到多条记录时脚本不自动选择先停住输出备选列表同时询问人工在终端输入序号确认。虽然多了一步交互但针对“课程名容易重复”的场景这个安全冗余是值得的。后来我还给 course.py 增加了缓存功能把匹配结果按课程名存成 JSON下次运行同一门课直接复用人工确认过的结果不用重复操作。5.4 明明有余量却提交选课失败这种情况有两种原因。第一是选课条件不满足比如有先修课程没通过、专业限制不对或者该课程只对特定年级开放。第二种原因是提交参数缺少必要字段比如必须勾选的“同意调课”复选框对应的请求参数没有带上。排查思路很直接先用浏览器手工选一次这门课注意观察网络面板里发出的请求把里面选课接口的参数和脚本发出去的参数做逐一对比。我按照这个思路解决过两次问题一次是漏传了学期字段一次是漏传了选课类型字段加上之后立刻恢复正常。6. 合规使用与个人实操心得6.1 脚本的合规边界和使用原则关于 BUPTtakeCourse 这类脚本我想认真地提醒几点。它只能用于自己的账号只能用于学校允许你选的课程只能用于正常补选需求。不能拿它去大量挤占名额、替别人刷课、或者以任何方式影响选课系统的公平性。我明确拒绝了几个功能需求批量替多个账号抢课、在未开放时段强行请求、绕过课程限制条件。这些功能技术上都能做但越过安全的边界就意味着给自己找麻烦。选课名额是公共资源脚本是公平竞争的手速增强器而不是破坏规则的工具。务必确认自己是否了解并接受学校对自动选课的相关管理规定。6.2 我在实际调试中踩过的坑第一次写登录模块时一直卡在密码加密上后来通过断点调试在浏览器开发者工具里查看请求提交的参数名才发现加密字段名和普通密码字段不同处理起来就顺畅了。这个经验对任何校内脚本都适用先用浏览器手动操作一次在开发者工具的 Network 面板里观察请求和响应大多数接口规律都能摸清楚。另一个坑是本地时间与服务器时间不一致导致监控提前进入高频状态。当时看着日志里到了设定时间但没有余量空跑了一个小时。后来增加了一次 NTP 校准——从系统响应头里读取服务器时间字段虽然差异通常只有几秒但对抢课来说已经足够了。6.3 后续可以怎么扩展如果只想解决一门课的问题现在的代码已经够了。但如果你想把这个项目当成一个长期维护的工具我会建议做这几件事。第一是增加 Web 配置界面不用每次改 YAML 文件第二是把轮询状态机抽象成更通用的选课引擎适配其他学校的教务系统第三是增加重试退避策略的单元测试保证频繁重构时核心逻辑不会被改坏。如果你只是想安稳选上一门课不需要把这些扩展全部做出来把 config.yaml 配好、跑一次看日志就够了。希望我踩过的这些坑能帮你少熬几个选课季的夜。本文还有配套的精品资源点击获取
返回列表