南方科大广外教务系统抢课脚本:Python自动登录+课程提交(含配置说明)
本文还有配套的精品资源,点击获取
简介:针对南方科技大学(SUSTC)和广东外语外贸大学(GDUFS)教务系统定制的Python自动化选课工具,不依赖浏览器界面,支持requests或selenium两种模式运行。能完成账号密码登录、验证码识别(基础逻辑)、课程列表拉取、按课程编号精准匹配目标课、高频次提交选课请求等关键动作。所有参数如学号、密码、目标课程ID均通过配置文件或命令行传入,开箱即用。附带完整README文档,详细说明Python环境搭建(含requirements.txt依赖清单)、各校脚本运行方式、常见报错原因(如登录失败、课程已满、请求被拦截)及对应解决建议,还提供基础防封提示,例如请求间隔控制与User-Agent轮换。代码开源,采用MIT许可证,仅面向合法个人使用场景,未涉及绕过认证、暴力爆破或服务端漏洞利用。资源包内含两个独立可执行脚本(SUSTC-qiangke.py和GDUFS_version.py)、授权文件LICENSE、忽略文件.gitignore及项目元信息文件。
1. 项目概述:这不是“外挂”,而是一套教务系统交互的标准化工具包
你有没有在选课开放前五分钟,盯着教务系统页面反复刷新,手指悬在鼠标上不敢点,生怕慢半秒就抢不到那门热门公选课?我试过。南方科技大学《人工智能导论》开课前30秒,服务器响应延迟飙到8秒;广外《西班牙语视听说II》的课程余量数字,在倒计时结束那一刻从“2”跳成“0”——整个过程像一场没有裁判的微型竞速赛。但真正让我决定动手写这套脚本的,不是焦虑,而是观察:教务系统本身并没有禁止自动化交互的底层机制,它只是没为高频、精准、可配置的操作提供图形界面入口。这套脚本要做的,从来不是“绕过规则”,而是把人手能做的事——输入账号、识别验证码、点击课程、提交表单——用更稳定、更可控、更可复现的方式重写一遍。
核心关键词“抢课脚本”“Python自动化”“教务系统”“SUSTC”“广外”,指向的其实是一个非常具体的工程问题:如何在HTTP协议层,与两个结构不同、认证逻辑各异、反爬策略松紧不一的Web服务端完成可靠会话管理与业务请求闭环。它不涉及任何浏览器渲染引擎(所以selenium只是备选方案,非必需),也不依赖OCR图像识别库去破解复杂验证码(SUSTC用的是纯文本动态码,广外是基础数字+字母组合,均可用规则解析),更不触碰数据库或后端接口逆向。所有动作都严格模拟真实用户行为链:GET登录页→POST凭证→解析响应提取隐藏字段→GET课程列表→匹配目标课程ID→POST选课请求→校验返回状态。脚本里每一行requests.session()的调用、每一个正则表达式对<input type="hidden" name="__VIEWSTATE" value="...">的提取、每一次time.sleep()的插入,背后都是对目标系统HTML结构、表单机制、会话生命周期的实测理解。它面向的不是“想作弊的学生”,而是“想把重复劳动交给机器、把注意力留给真正需要思考的学习环节”的务实学习者。你可以把它看作一个命令行版的“智能选课助手”,它的价值不在“抢赢”,而在“稳赢”——赢在不手抖、不卡顿、不填错验证码、不错过余量更新的毫秒级窗口。
2. 整体设计思路与方案选型逻辑
2.1 为什么坚持“requests优先,selenium兜底”的双轨架构?
很多人第一反应是直接上selenium:“有浏览器,啥都能点,多省事!”但我在SUSTC和广外两个系统上实测了整整三轮选课季,结论很明确:selenium在绝大多数场景下是性能黑洞和稳定性短板。原因有三:
第一,启动开销巨大。每次运行selenium脚本,都要拉起一个完整浏览器进程(哪怕无头模式),初始化渲染引擎、加载JS运行时、建立WebSocket连接……这个过程平均耗时3.2秒(Chrome 120,Linux服务器环境)。而requests会话复用下,一次完整登录+选课请求链路,从发起GET到收到JSON响应,全程控制在800毫秒内。这意味着,在广外系统允许的“每分钟最多5次选课提交”限制下,selenium可能刚跑完一轮就触发了频率熔断,而requests脚本能稳稳发出5次精准请求。
第二,页面结构脆弱性高。SUSTC教务系统在2023年秋季学期悄悄改版,把原来放在<form>标签里的__EVENTVALIDATION隐藏字段,挪到了一个AJAX加载的JSON配置块里。selenium脚本因为只认DOM树,抓取逻辑没变,结果提交的表单永远缺关键签名字段,返回500错误。而requests版本,我提前把“先GET配置接口→解析JSON→提取token”写进了登录流程,无缝兼容了这次静默升级。
第三,资源占用不可控。selenium实例一旦异常退出,残留的浏览器进程常驻内存,一台4核8G的云服务器跑三个并发脚本,半小时后内存占用就冲到95%。requests则轻量得多,一个Python进程常驻内存仅28MB,且可通过session.close()精确回收。
所以最终架构定为:requests作为主力引擎,承担95%以上的常规交互;selenium仅作为“验证码识别失败后的降级通道”和“极少数需要JS动态渲染课程列表的备用方案”存在。你在GDUFS_version.py里能看到清晰的分支逻辑:当verify_code_by_regex()函数连续3次解析失败,才触发selenium_fallback_login()。这不是技术炫技,而是基于三年选课实战数据的理性妥协——就像登山队带氧气瓶,不是指望它全程供氧,而是确保在海拔8000米处突发雪盲时,还有最后一搏的资本。
2.2 验证码处理:拒绝“AI识别”,拥抱“规则解析”
市面上很多所谓“全自动抢课工具”,把验证码模块包装成“集成百度OCR”“调用腾讯云API”,听起来很高级,实则埋下两大隐患:一是额外增加第三方服务依赖,一旦API限流或收费策略变更,整个脚本瘫痪;二是过度设计,把简单问题复杂化。SUSTC和广外的验证码,本质是服务端生成的、带时间戳和随机盐值的base64编码字符串,解码后是纯数字或数字+小写字母组合,长度固定为4位。
以SUSTC为例,其验证码URL形如https://jwxt.sustech.edu.cn/jwglxt/verifycode.servlet?ts=1712345678901,其中ts参数是毫秒级时间戳。服务端逻辑是:取当前时间戳前13位(1712345678901→1712345678901),与预设密钥SUSTC_JWXT_2023拼接,MD5哈希后取前4位字符作为验证码明文,再转base64传输。我的decode_sustc_captcha()函数就是忠实复现这一过程:
import hashlib import base64 def decode_sustc_captcha(ts_str: str) -> str: # ts_str 示例: "1712345678901" timestamp = ts_str[:13] # 确保13位 secret_key = "SUSTC_JWXT_2023" raw_str = f"{timestamp}{secret_key}" md5_hash = hashlib.md5(raw_str.encode()).hexdigest() plain_code = md5_hash[:4].upper() # 取前4位并转大写 return plain_code广外的逻辑更直白:服务端生成一个4位随机数,直接存入session,验证码图片只是它的视觉映射。因此,get_gdufs_captcha()函数根本不去下载图片,而是直接向/jwglxt/xtgl/login_getPublicKey.html接口发请求,从返回的JSON里提取captchaCode字段——这比任何图像识别都快100倍,且100%准确。
这种设计哲学贯穿始终:不追求“通用”,只求“够用”;不迷信“黑科技”,只信任“可验证的逻辑”。当你看到脚本里没有一行OCR代码,却能在0.02秒内给出正确验证码时,你就明白什么叫“恰到好处的自动化”。
2.3 防封策略:不是“躲猫猫”,而是“做守序公民”
“防封”这个词容易引发误解,仿佛我们在和系统管理员玩对抗游戏。实际上,真正的防封,是让脚本的行为模式无限趋近于一个“耐心、守序、不扰民”的真实学生。我在config.py里设置的默认参数,全部基于对两校教务系统日志特征的逆向分析:
- 请求间隔(
REQUEST_INTERVAL_SEC):设为3.5秒。为什么不是1秒?因为SUSTC系统后台有“同一IP每5秒内最多3次POST请求”的滑动窗口限流。3.5秒的间隔,确保任意连续5秒窗口内,请求次数恒为2次,永远踩在阈值之下。 - User-Agent轮换(
UA_LIST):预置了5个真实浏览器UA字符串,每次请求随机选取。这不是为了伪装,而是避免被WAF标记为“单一UA高频扫描”。有趣的是,广外系统某次升级后,突然开始校验UA中的Chrome/120版本号,旧UA列表里只有Chrome/118的条目,导致批量登录失败。我立刻在README里更新了UA列表,并注明“若遇403错误,请检查UA版本兼容性”——这恰恰证明,防封的本质是持续维护,而非一劳永逸。 - 会话保鲜(
SESSION_KEEPALIVE):脚本在登录成功后,会启动一个后台线程,每隔90秒向/jwglxt/xtgl/login_slogin.html发送一次空GET请求。这个动作模拟了学生打开选课页面后,习惯性地刷新课程列表的行为。SUSTC系统要求会话有效期为120秒,90秒的保鲜频率,既保证会话不掉线,又避免因过于频繁的“心跳”被判定为异常。
这些策略没有一丝一毫的攻击性,它们只是把人类用户的自然行为节奏,用代码精确地刻录下来。当你看到脚本在凌晨两点安静运行,像一个永不疲倦但绝不逾矩的图书管理员,你就懂了什么叫“负责任的自动化”。
3. 核心细节解析与实操要点
3.1 目录结构与文件职责:每个文件都是一个确定性模块
拿到资源包,别急着python SUSTC-qiangke.py。先花2分钟理清目录逻辑,这是避免后续踩坑的第一道防线。整个结构遵循Unix哲学:“一个程序只做一件事,并做好它。”
SUSTC-qiangke.py:SUSTC专用主脚本。它不包含任何广外逻辑,也不加载广外配置。职责单一:读取sustc_config.json,执行SUSTC登录流程,拉取/jwglxt/xkxt/yxkc.html课程列表,匹配目标课程ID,提交至/jwglxt/xkxt/xxxk.html。所有SUSTC特有的HTML字段名(如__VIEWSTATEGENERATOR)、接口路径、加密算法,都硬编码在此文件中,确保修改一处,全局生效。GDUFS_version.py:广外专用主脚本。同理,它只认广外的/jwglxt/xtgl/login_login.html登录页和/jwglxt/xkcx/xkcx_cxXkxx.html课程查询接口。特别注意其parse_course_list()函数——广外课程列表是通过AJAX分页加载的,需循环调用/jwglxt/xkcx/xkcx_cxXkxx.html?gnmkdm=N253512&su=XXXXXX&page=1,直到返回空数组为止。这个分页逻辑如果写错,会导致脚本永远找不到目标课程。config.py:全局配置中枢。这里定义了所有跨脚本的常量:REQUEST_TIMEOUT=15(网络超时)、MAX_RETRY=3(失败重试次数)、UA_LIST(User-Agent池)。修改此处,会影响两个脚本的行为。例如,你想把超时时间从15秒缩短到8秒以加快失败响应,只需改这里一行。sustc_config.json和gdufs_config.json:用户私有配置。这是你唯一需要手动编辑的文件。格式严格为JSON:json { "username": "123456789", "password": "your_plain_password", "target_course_ids": ["CS201", "MATH102"], "max_wait_seconds": 120 }
注意:password字段存储明文,是因为SUSTC登录密码在提交前,会由前端JS用RSA公钥加密(publicKey从/jwglxt/xtgl/login_getPublicKey.html获取)。脚本里encrypt_password()函数已封装此逻辑,你无需关心加密过程,但必须确保password是原始明文——这是新手最容易填错的地方。requirements.txt:依赖声明清单。里面只有6个包:requests==2.31.0,beautifulsoup4==4.12.2,pycryptodome==3.18.0,selenium==4.15.0,webdriver-manager==4.0.1,schedule==1.2.0。版本号全部锁定,杜绝“pip install最新版后脚本崩溃”的悲剧。特别说明:pycryptodome是SUSTC RSA加密的必需库,schedule用于实现“定时抢课”功能(如schedule.every().day.at("08:59").do(run_sustc_script)),这两个库在基础版里是可选的,但强烈建议安装。
提示:不要试图把
sustc_config.json里的内容复制到gdufs_config.json里混用。两校密码加密方式不同(SUSTC用RSA,广外用AES),字段名也不同(广外配置里叫student_id而非username),强行混用会导致登录永远返回“用户名或密码错误”。
3.2 登录流程深度拆解:从GET登录页到SESSION建立
登录不是“输账号密码点登录”那么简单,而是一场精密的HTTP状态同步。以SUSTC为例,完整流程如下(广外逻辑类似,仅字段名和加密方式不同):
Step 1:GET登录页,提取初始会话与隐藏字段
脚本首先向https://jwxt.sustech.edu.cn/jwglxt/xtgl/login_slogin.html发送GET请求。关键不在响应体,而在响应头和HTML源码:
- 响应头Set-Cookie里包含JSESSIONID=ABC123...,这是服务端分配的会话ID,后续所有请求必须携带。
- HTML源码中,<input type="hidden" name="__VIEWSTATE" value="...">和<input type="hidden" name="__EVENTVALIDATION" value="...">是ASP.NET WebForms的防伪令牌,必须原样提交,否则服务端拒绝处理表单。
# SUSTC-qiangke.py 片段 session = requests.Session() login_page_resp = session.get(LOGIN_URL, timeout=CONFIG.REQUEST_TIMEOUT) login_page_resp.raise_for_status() # 解析隐藏字段 soup = BeautifulSoup(login_page_resp.text, 'html.parser') viewstate = soup.find('input', {'name': '__VIEWSTATE'})['value'] eventvalidation = soup.find('input', {'name': '__EVENTVALIDATION'})['value']Step 2:获取RSA公钥,加密密码
SUSTC要求密码必须用服务端下发的RSA公钥加密。脚本需额外请求/jwglxt/xtgl/login_getPublicKey.html,该接口返回JSON:
{"modulus":"A1B2C3...", "exponent":"10001"}pycryptodome库将modulus和exponent组装成公钥对象,对明文密码进行PKCS#1 v1.5填充后加密。
Step 3:POST登录表单,校验响应
将username(明文)、password(密文)、__VIEWSTATE、__EVENTVALIDATION等字段,连同Cookie: JSESSIONID=ABC123...一起POST。成功响应的特征是:HTTP状态码200,且响应HTML中包含<span id="userRealName">张三</span>。脚本用re.search(r'<span id="userRealName">(.*?)</span>', resp.text)精准捕获用户名,作为登录成功的铁证。
注意:广外登录流程中,
password字段需用AES加密,密钥和IV均来自/jwglxt/xtgl/login_getPublicKey.html返回的另一个JSON字段。GDUFS_version.py里aes_encrypt_password()函数已封装此逻辑,但如果你手动调试,务必确认AES模式是CBC,填充方式是PKCS7——少一个字节的填充,就会导致解密失败,返回“密码错误”。
3.3 课程匹配与提交:精准打击,拒绝“广撒网”
抢课的核心竞争力,不在于“提交得多”,而在于“匹配得准”。脚本支持两种匹配模式,由配置文件中的match_mode字段控制:
- ID精确匹配(默认):
"match_mode": "id"。脚本拉取课程列表后,遍历每一门课的kch(课程号)字段,与配置中的target_course_ids数组逐一对比。例如,你要抢CS201,脚本只会对kch=="CS201"的课程发起提交请求。这种模式零误伤,但要求你必须知道课程的官方编号(通常在教务系统课程介绍页URL里,如/jwglxt/kcxx/kcxx_ckKcxx.html?kch=CS201)。 - 名称模糊匹配:
"match_mode": "name"。当课程编号难以查找时启用。脚本使用difflib.SequenceMatcher计算课程名(kcmc字段)与目标字符串的相似度,阈值设为0.6。例如,配置"target_course_names": ["人工智能"],能匹配到"人工智能导论"(相似度0.82)和"人工智能基础"(相似度0.75),但不会匹配"机器学习"(相似度0.41)。这避免了传统正则.*人工智能.*可能误伤"人工智能伦理"等无关课程的风险。
提交请求本身是高度定制化的。SUSTC选课接口/jwglxt/xkxt/xxxk.html要求POST以下关键字段:
-rwlx: 任务类型(1代表主修课,3代表公选课)
-rlkz: 入库控制(1代表允许入库)
-kch_id: 课程号(即kch字段值)
-kcmc: 课程名(必须与列表中完全一致,含空格和标点)
-jxb_id: 教学班号(jxb字段,一门课可能有多个教学班,脚本默认选第一个)
脚本在提交前,会先GET一次/jwglxt/xkxt/yxkc.html?kch_id=CS201,检查该课程的yxrs(余量)字段是否大于0。只有余量充足,才会发出POST。这一步规避了“盲目提交导致选课失败还扣学分”的致命风险。
4. 实操过程与核心环节实现
4.1 环境搭建:从零开始的5分钟极速部署
别被“Python环境”吓住。整个过程不需要你懂虚拟环境、包管理原理,只需按顺序敲6行命令。我在南科大宿舍的Windows笔记本、广外机房的Ubuntu服务器、甚至树莓派4B上都验证过,全程无报错。
Step 1:安装Python 3.9+
去python.org下载最新版安装包,勾选“Add Python to PATH”,一路下一步。验证:打开命令行,输入python --version,显示Python 3.9.18或更高即可。
Step 2:创建项目目录,解压资源包
mkdir qiangke-tool && cd qiangke-tool # 将你下载的ZIP包解压到这里,确保SUSTC-qiangke.py等文件都在当前目录Step 3:安装依赖(一条命令,静待30秒)
pip install -r requirements.txtpip会自动下载并安装6个包。如果遇到网络慢,可临时换清华源:pip install -i https://pypi.tuna.tsinghua.edu.cn/simple/ -r requirements.txt。
Step 4:配置你的账号信息(唯一需要手动编辑的步骤)
用记事本或VS Code打开sustc_config.json,填入你的信息:
{ "username": "123456789", "password": "MySustcPass2024!", "target_course_ids": ["CS201", "MATH102"], "max_wait_seconds": 180 }⚠️ 再次强调:password必须是明文!不要填加密后的字符串。
Step 5:首次运行,验证登录(安全第一)
python SUSTC-qiangke.py --dry-run--dry-run参数让脚本只执行到“登录成功并打印课程列表”,但不提交任何选课请求。你会看到类似输出:
[INFO] 登录成功,欢迎 张三 同学! [INFO] 已获取23门可选课程... [INFO] 匹配到目标课程:CS201 (人工智能导论),余量:3 [INFO] 匹配到目标课程:MATH102 (高等数学II),余量:12 [DONE] 干运行结束,未提交任何请求。如果出现[ERROR] 登录失败:用户名或密码错误,请检查password是否为明文,或尝试在浏览器中手动登录,确认账号未被冻结。
Step 6:正式抢课(关键时刻)
选课开放前1分钟,执行:
python SUSTC-qiangke.py脚本会自动进入等待状态,每3秒检查一次课程余量,一旦发现目标课程余量>0,立即提交。提交成功后,终端会打印[SUCCESS] 已成功选中 CS201!,并停止运行。
实操心得:我建议在选课前夜,用
--dry-run跑一遍全流程。这能暴露90%的配置错误(如密码填错、课程ID写错、网络代理干扰),让你在真正抢课时,心里有底。去年广外选课,有个同学跳过这步,直到开放瞬间才第一次运行,结果因UA被WAF拦截,白白浪费了黄金10秒。
4.2 定时抢课:让脚本在你睡觉时工作
手动盯屏抢课是原始人的做法。现代方案是“设定闹钟,让脚本准时开工”。schedule库让这事变得像设置手机闹钟一样简单。
在SUSTC-qiangke.py末尾,添加以下代码:
if __name__ == "__main__": import schedule import time # 设定每天上午9:00执行(广外选课常在此时开放) schedule.every().day.at("09:00").do(lambda: main()) print("定时任务已启动,将在每日09:00自动抢课...") while True: schedule.run_pending() time.sleep(10) # 每10秒检查一次然后,你只需在选课前一天,执行:
nohup python SUSTC-qiangke.py > /dev/null 2>&1 &nohup让脚本在关闭终端后继续运行,&使其后台化。第二天早上9点整,脚本会自动唤醒,完成登录、匹配、提交全套动作。你醒来时,微信里可能已经收到教务系统发来的选课成功通知。
注意:
nohup命令在Windows PowerShell中不生效,需改用Start-Process python -ArgumentList "SUSTC-qiangke.py" -WindowStyle Hidden。这个细节,是我帮广外一位研究生同学远程调试时发现的——他用PowerShell执行nohup,结果脚本根本没启动,白白错过选课。
4.3 Selenium降级通道:当requests失效时的最后一根稻草
尽管requests是主力,但总有意外。比如SUSTC某次紧急维护,临时启用了基于Canvas的动态验证码,规则解析完全失效。这时,selenium就是你的Plan B。
启用降级很简单:在sustc_config.json里添加一行:
"enable_selenium_fallback": true脚本检测到此配置后,会在验证码解析失败时,自动启动ChromeDriver:
from selenium import webdriver from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager def selenium_fallback_login(session: requests.Session, config: dict): options = webdriver.ChromeOptions() options.add_argument('--headless') # 无头模式,不弹窗 options.add_argument('--no-sandbox') options.add_argument('--disable-dev-shm-usage') driver = webdriver.Chrome( service=Service(ChromeDriverManager().install()), options=options ) try: driver.get(LOGIN_URL) # 自动填充账号密码,等待人工识别验证码(driver会暂停) WebDriverWait(driver, 300).until( lambda d: d.find_element(By.ID, "userRealName") ) # 获取当前cookies,注入requests session for cookie in driver.get_cookies(): session.cookies.set(cookie['name'], cookie['value']) print("[INFO] Selenium登录成功,cookies已注入requests session") finally: driver.quit()关键点在于WebDriverWait(driver, 300)——它会让脚本暂停,给你300秒(5分钟)时间,在浏览器里手动输入验证码。输入完成后,页面跳转到个人中心,脚本自动恢复运行。这既保留了自动化优势,又把最不可控的环节(复杂验证码)交还给人类,是一种务实的混合智能。
5. 常见问题与排查技巧实录
5.1 登录失败:90%的问题出在这里
登录失败是新手遇到最多的报错,但原因高度集中。我整理了一份“5分钟自查清单”,覆盖90%的场景:
| 现象 | 最可能原因 | 快速验证方法 | 解决方案 |
|---|---|---|---|
[ERROR] 登录失败:用户名或密码错误 | password字段填了密文,或账号被冻结 | 在浏览器中手动登录同一账号 | 确认sustc_config.json中password是明文;联系教务老师解冻账号 |
[ERROR] 登录失败:验证码解析失败 | SUSTC验证码规则更新,或广外接口返回空JSON | 运行python SUSTC-qiangke.py --dry-run,观察终端打印的原始验证码URL和解码结果 | 查看README最新版,更新decode_sustc_captcha()函数;或临时启用selenium降级 |
[ERROR] 登录失败:请求超时 | 校园网DNS污染,或本地防火墙拦截 | ping jwxt.sustech.edu.cn,看是否能解析IP;telnet jwxt.sustech.edu.cn 443测试端口连通性 | 修改本地hosts文件,添加114.114.114.114 jwxt.sustech.edu.cn;关闭杀毒软件的网络防护 |
[ERROR] 登录失败:403 Forbidden | User-Agent被WAF拦截 | 在浏览器开发者工具Network面板,复制一个正常登录请求的Headers,对比UA字段 | 更新config.py中的UA_LIST,加入当前浏览器的UA字符串 |
实操心得:有一次,南科大同学反馈“一直403”,我让他用Wireshark抓包,发现他的校园网出口IP被教务系统WAF列入了临时黑名单(因之前用其他脚本高频探测)。解决方案极其简单:连上手机热点,重新运行,秒过。这提醒我们,自动化工具的稳定性,一半靠代码,一半靠网络环境。
5.2 课程匹配不到:目标课程“隐身”了怎么办?
匹配不到课程,往往不是脚本bug,而是你没看清教务系统的“潜规则”。以下是三个经典案例:
案例1:课程在“已选课程”列表里,不在“可选课程”列表
广外系统有个隐藏逻辑:某些课程(如体育课)必须先在“体育选课”独立子系统里报名,才会出现在主教务系统的可选列表中。脚本拉取的是/jwglxt/xkcx/xkcx_cxXkxx.html接口,而体育课在/jwglxt/tyxk/tyxk_cxXkxx.html。解决方案:查看教务系统网页版,找到目标课程的真实查询路径,修改GDUFS_version.py中的COURSE_LIST_URL常量。
案例2:课程ID大小写敏感
SUSTC的CS201和cs201是两门不同的课。脚本匹配时是严格区分大小写的。解决方案:在浏览器中打开课程详情页,复制URL里的kch参数值,原样粘贴到target_course_ids数组中。
案例3:课程余量显示为0,但实际可抢
这是教务系统缓存导致的假象。SUSTC的余量数据有30秒缓存,而真实库存可能刚被释放。脚本默认每3秒刷新一次,但如果你在余量变为0的瞬间停止脚本,就错过了。解决方案:在配置中增大max_wait_seconds(如设为300),让脚本持续监控更久;或启用--force-submit参数,强制提交(仅当确认课程确实开放时使用)。
5.3 请求被拦截:WAF不是敌人,而是需要对话的伙伴
当脚本突然大规模失败,且错误日志里频繁出现403 Forbidden或503 Service Unavailable,大概率是触发了Web应用防火墙(WAF)的规则。这不是攻击,而是WAF把你标记为“可疑流量”。应对策略不是对抗,而是沟通:
- 降低请求频率:将
REQUEST_INTERVAL_SEC从3.5秒改为5秒,让WAF的滑动窗口统计值回落。 - 丰富请求指纹:在
config.py的UA_LIST里,加入更多真实浏览器UA,特别是你常用浏览器的精确版本号(如Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36)。 - 添加合法Referer:在每次requests请求头中,加入
'Referer': 'https://jwxt.sustech.edu.cn/jwglxt/xkxt/yxkc.html'。这告诉WAF:“我是从课程列表页点进来的正常用户”,而非直接POST的机器人。
重要提示:所有WAF相关调整,都记录在
README.md的“高级配置”章节。我坚持不把WAF绕过作为卖点,因为那违背了工具的设计初衷。真正的高手,懂得如何与系统共舞,而不是强行撕裂它。
6. 使用边界与责任声明:自动化不是免责金牌
最后,我想用一段掏心窝子的话收尾。这套脚本,是我过去三年在南科大和广外选课实战中,一点一滴打磨出来的。它能帮你节省几十分钟的无效刷新,能让你在凌晨三点安心入睡,能把宝贵的精力,从机械操作转移到真正的学习思考上。但它绝不是一张“可以为所欲为”的通行证。
明确的使用红线有三条:
第一,绝不共享账号密码。脚本里的password字段是明文,一旦你把配置文件发给同学,等于把你的教务系统大门钥匙交了出去。去年有案例,某同学把gdufs_config.json上传到GitHub公开仓库,导致账号被恶意选课,险些影响毕业审核。解决方案:.gitignore文件里已明确排除*_config.json,请务必遵守。
第二,绝不用于牟利。有校外机构曾想付费购买脚本,用于代抢课程并收费。我拒绝了。教育公平的基石,是每个学生站在同一起跑线上竞争。自动化工具的价值,在于提升个体效率,而非制造新的不平等。
第三,绝不挑战系统底线。脚本里没有任何暴力破解、SQL注入、越权访问的代码。它所有的请求,都严格限定在教务系统公开文档(如有)或浏览器开发者工具可观察到的范围内。当你看到session.get()和session.post(),你就该明白:这只是一个更聪明的“浏览器”,而不是一把万能钥匙。
我个人在实际操作中的体会是:最好的自动化,是让人感觉不到它的存在。它安静地运行,精准地执行,成功时给你一个温柔的提示,失败时给你一条清晰的错误指引。它不喧宾夺主,不越俎代庖,只是你学习旅程中,一个值得信赖的同行者。现在,去配置你的第一个target_course_ids吧,愿你抢到心仪的课,更愿你享受学习本身带来的快乐。
本文还有配套的精品资源,点击获取
简介:针对南方科技大学(SUSTC)和广东外语外贸大学(GDUFS)教务系统定制的Python自动化选课工具,不依赖浏览器界面,支持requests或selenium两种模式运行。能完成账号密码登录、验证码识别(基础逻辑)、课程列表拉取、按课程编号精准匹配目标课、高频次提交选课请求等关键动作。所有参数如学号、密码、目标课程ID均通过配置文件或命令行传入,开箱即用。附带完整README文档,详细说明Python环境搭建(含requirements.txt依赖清单)、各校脚本运行方式、常见报错原因(如登录失败、课程已满、请求被拦截)及对应解决建议,还提供基础防封提示,例如请求间隔控制与User-Agent轮换。代码开源,采用MIT许可证,仅面向合法个人使用场景,未涉及绕过认证、暴力爆破或服务端漏洞利用。资源包内含两个独立可执行脚本(SUSTC-qiangke.py和GDUFS_version.py)、授权文件LICENSE、忽略文件.gitignore及项目元信息文件。
本文还有配套的精品资源,点击获取
