--- title: Advanced SCU Course Catcher emoji: 🐳 colorFrom: green colorTo: yellow sdk: docker app_port: 7860 pinned: false --- # Advanced SCU Course Catcher 这是一个面向 Hugging Face Space 的四川大学选课系统 Web 版抢课面板。当前版本已经从单机脚本改造成了可部署、可并行、可多用户管理的 Flask + Selenium 服务,支持普通用户端和管理员端分离运行。 ## 当前能力 - 普通用户入口:`/login` 用户使用 `学号 + 密码` 登录,只能看到自己的课程、任务状态和实时日志。 - 管理员入口:`/admin` 管理员使用 `账号 + 密码` 登录。此入口不会在用户登录页展示。 - 多管理员体系 支持多位普通管理员,支持 1 位超级管理员。超级管理员账号和密码由环境变量 `ADMIN`、`PASSWORD` 提供。 - 多用户管理 管理员可以手动录入用户账号、重置密码、启用/禁用用户、查看所有课程目标。 - 课程录入 用户自己填写 `课程号` 和 `课序号`,管理员可见全部内容,也可以代为录入和修改。 - 实时日志 用户和管理员都可以实时看到程序日志,前端通过 SSE 持续推送。 - 并行调度 多用户任务由后台任务调度器并行执行,管理员可以在后台动态设置并行数。 - Hugging Face Space 适配 使用 Docker Space 运行,内置 Chromium、chromedriver、中文字体和无头浏览器配置。 - 登录验证码与提交验证码 OCR 登录阶段和提交选课阶段都支持验证码 OCR 自动识别。 - Selenium 自恢复 单个 Selenium 会话连续错误达到 5 次时,会自动重建浏览器;连续重建 5 个会话仍失败时,任务才会终止。 ## 运行结构 当前实际运行入口和核心模块如下: - `app.py` Hugging Face / gunicorn 使用的 WSGI 入口。 - `space_app.py` Flask 路由、页面渲染、登录鉴权、SSE 日志流。 - `core/config.py` 运行配置与内部密钥管理。 - `core/db.py` SQLite 数据层。 - `core/task_manager.py` 并行调度、任务生命周期管理。 - `core/course_bot.py` Selenium 抢课核心逻辑、验证码识别、重试与重建策略。 - `webdriver_utils.py` Chromium / chromedriver 初始化。 - `templates/` + `static/` 用户端、管理员端和响应式前端页面。 ## 环境变量 为了减少 Space 配置复杂度,当前只保留少量必要环境变量。 ### 必填 - `ADMIN` 超级管理员账号。 - `PASSWORD` 超级管理员密码。 ### 可选 - `DATA_DIR` 数据目录,默认会使用仓库下的 `data/`。在 Hugging Face Space 中建议保持为 `/data`。 - `CHROME_BIN` 自定义 Chromium 路径。默认会自动尝试 `/usr/bin/chromium`。 - `CHROMEDRIVER_PATH` 自定义 chromedriver 路径。默认会自动尝试 `/usr/bin/chromedriver`。 ### 不需要再手动配置的内容 以下内容已经改为自动生成或写入程序默认值,不需要再手工配置环境变量: - Flask session secret - 用户密码加密密钥 - 默认并行数 - 登录重试次数 - Selenium 会话错误阈值 - Selenium 会话重建阈值 - 提交验证码重试次数 - 页面超时时间 - 日志页容量 程序会在 `DATA_DIR/.app_secrets.json` 中自动持久化内部密钥。这样即使以后修改 `ADMIN` 或 `PASSWORD`,也不会导致历史用户密码全部失效。 ## Hugging Face Space 部署 ### 1. 创建 Space 在 Hugging Face 上创建一个新的 Space: - SDK 选择 `Docker` - 端口使用 `7860` - 仓库内容直接推送本项目即可 本仓库根目录已经提供好 `README.md` 的 Space metadata 和 `Dockerfile`。 ### 2. 设置 Space Secrets 进入 `Settings -> Variables and secrets`,至少配置: - `ADMIN=你的超级管理员账号` - `PASSWORD=你的超级管理员密码` 通常不需要再设置其他环境变量。 如果你想显式指定数据目录,也可以增加: - `DATA_DIR=/data` ### 3. 开启持久化存储 如果你希望以下数据在 Space 重启后仍然保留,建议在 Space 设置里开启 Persistent Storage: - 用户账号 - 管理员账号 - 课程目标 - 任务历史 - 运行日志 - 自动生成的内部密钥文件 当前 Dockerfile 已按 `/data` 目录进行适配,并预先处理了目录权限,方便在 Space 中直接持久化。 ### 4. 推送部署 如果你使用 git 推送到 Hugging Face Space,可以参考: ```bash git remote add space https://huggingface.co/spaces// git push space main ``` ### 5. 启动方式 容器启动命令已经写入 `Dockerfile`: ```bash gunicorn --bind 0.0.0.0:${PORT:-7860} --workers 1 --threads 8 --timeout 180 app:app ``` 这里使用 `1` 个 gunicorn worker,是为了避免多进程情况下每个进程都各自启动一套本地任务调度器。并发能力由应用内部的线程调度和管理员可配置的任务并行数控制。 ## Space 端建议配置 推荐的初始运行策略: - 后台并行数先设置为 `1` 或 `2` - 只有在 Space CPU / 内存足够稳定时,再逐步提升 - 在真实高峰前,先做一次小规模联调 对于 CPU 较小的 Space,同时拉起过多 Chromium 会明显增加失败率,因此管理员后台提供了并行数动态调整功能。 ## 管理员联调清单 部署到 Space 后,建议按以下顺序联调: 1. 访问 `/admin`,使用 `ADMIN` / `PASSWORD` 登录超级管理员。 2. 在管理员后台创建 1 个普通管理员、2 个测试用户。 3. 为两个测试用户分别录入不同课程号和课序号。 4. 将并行数设置为 `2`。 5. 分别触发两个用户任务,确认两条任务都能进入队列,并按并行数启动。 6. 在管理员日志面板确认日志持续刷新,且可以看到不同学号的执行记录。 7. 使用用户账号访问 `/login`,确认用户只能看到自己的课程和日志。 8. 在任务运行过程中测试停止任务,确认状态能变为“停止中”并最终收敛。 9. 如果学校页面出现提交验证码,确认日志中能看到提交阶段验证码识别提示。 ## 自动化测试 仓库当前已补充以下自动化测试: - `tests/test_config.py` 验证内部密钥在超级管理员账号变更后依然保持稳定。 - `tests/test_course_bot.py` 验证 Selenium 会话错误累计后会触发浏览器重建,并验证提交阶段验证码分支。 - `tests/test_task_manager.py` 验证任务调度器会严格遵守管理员设置的并行上限。 - `.github/workflows/linux-tests.yml` 在 `ubuntu-latest` 上自动运行上述测试,避免关键逻辑只在 Windows 本地验证。 本地运行测试: ```bash python -m unittest discover -s tests -v ``` ## 本地运行 ### 直接运行 ```bash pip install -r requirements.txt set ADMIN=admin set PASSWORD=change-me python main.py ``` ### Docker 运行 ```bash docker build -t advanced-scu-course-catcher . docker run --rm -p 7860:7860 \ -e ADMIN=admin \ -e PASSWORD=change-me \ advanced-scu-course-catcher ``` ## 重要说明 - 本项目当前的任务模型是“持续轮询直到成功、手动停止或达到错误上限”。 - 用户密码会以应用内部密钥加密后保存在数据库中,用于后台自动登录教务系统。 - 管理员可以看到全部用户、课程和日志,请在实际使用前明确权限边界。 - 如果学校选课页面 DOM 结构变化较大,需要同步更新 `core/course_bot.py` 中的选择器以及 `javascript/` 下的辅助脚本。 - `course_catcher/` 目录是仓库中保留的旧实现,当前运行链路以 `space_app.py + core/*` 为准。