自动预约大学体育场馆羽毛球场地,提供 3 种抢场方式,适应不同场景和平台。
| 方式 | 平台 | 原理 | 速度 | 适用场景 |
|---|---|---|---|---|
| Safari 自动点击 | macOS | AppleScript 控制 Safari + JS 注入点击页面 DOM | 中等 | 无需逆向 API,所见即所得 |
| API 直连 | macOS / Windows | 逆向 API + Playwright CAS 登录 → 直接 HTTP 请求 | 最快 | 追求极致速度,可跨平台 |
| Android App | Android | 同上 API + WebView CAS 登录 → OkHttp 请求 | 快 | 移动端,随时随地抢场 |
- ⚡ 毫秒级抢场 —— API 直连模式绕过浏览器,直接 HTTP 请求,~200ms 响应
- 🧠 智能选场策略 —— 多维度优先级评分(不靠墙优先 / 靠墙保底),Top-N 备选 + 冲突自动切换
- 🔄 候补轮询 —— 循环查场,目标时间段一有空场立即下手
- ⏰ 定时启动 —— 提前运行,到预设放场时间自动开始
- 📢 钉钉通知 —— 实时推送抢场状态(启动、无场、冲突、成功),支持 HMAC 加签
- 🎛️ Web 图形界面 —— 浏览器端可视化配置所有参数,一键保存并启动
- 🔑 自动登录 —— Playwright / WebView 完成 CAS 统一认证,Token 缓存 5 小时
- 🔐 请求签名 —— 完整还原前端签名算法(MD5),无惧接口加密
通过 AppleScript 控制 Safari 浏览器,注入 JavaScript 读取 Vue 渲染后的 DOM,解析场地时间格,自动点击完成预约。
- macOS + Safari
- Safari 菜单「开发」→ 勾选「允许 Apple 事件中的 JavaScript」
- Python 3.9+
cd safari_booking
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtcd safari_booking && source .venv/bin/activate
# 查看当前可订场地(不自动点击)
python3 scripts/check_court_availability.py
# 查看 + 自动点击预约
python3 scripts/check_court_availability.py --click
# 详细诊断模式(抢场失败时打印更多信息)
python3 scripts/check_court_availability.py --click -v
# 保存 HTML 快照(不解析)
python3 scripts/check_court_availability.py --save-only也可以从项目根目录通过 config_ui.py 一键启动(含自动登录):
python3 config_ui.py
# 在浏览器中配置参数后,点击「保存并启动抢场 (Safari)」
### 候补模式
在目标时间窗口内循环刷新检测,一旦有空场立即抢:
```bash
cd safari_booking && source .venv/bin/activate
python3 scripts/standby_court.py
python3 scripts/standby_court.py --start 19:00 --end 22:00
python3 scripts/standby_court.py -v --no-refresh --max-rounds 50| 参数 | 说明 | 默认值 |
|---|---|---|
PLAY_WINDOW_START / _END |
偏好时间窗口 | 19:00 ~ 20:00 |
WALL_COURT_NUMBERS |
靠墙场地编号列表 | 见脚本 |
WAIT_UNTIL_ENABLED |
是否等到指定时刻再执行 | True |
WAIT_UNTIL_CLOCK |
目标执行时间 | 16:00 |
REFRESH_DATE_BUTTON_INDEX |
刷新时选第几个日期(1=今天, 2=明天) | 2 |
Safari 标签页(Vue SPA)
→ AppleScript do JavaScript → 抓取渲染后 DOM HTML
→ BeautifulSoup 解析 div.sites-item → CourtFree 数据类
→ decide_booking() 选择最佳方案:
窗口 ≥2h:偏好连续两小时
否则:最优单小时
平局规则:不靠墙 > 靠墙,低场地号 > 高场地号,早开始 > 晚开始
→ (--click) JS 注入点击时间格 → 点击「预约」按钮 → 确认弹窗
逆向场馆预约系统的后端 API,直接用 HTTP 请求完成登录、查场、预约,比浏览器操作快得多。
cd api_reverse
python3 -m venv .venv
source .venv/bin/activate
pip install -r scripts/requirements.txt
pip install playwright
playwright install chromium项目根目录下创建 credentials.json(已加入 .gitignore):
{
"username": "你的学号",
"password": "你的密码"
}# 图形配置界面(浏览器打开,可视化设置所有参数)
# 从项目根目录运行(无需 venv)
python3 config_ui.py
# 命令行直接启动
cd api_reverse && source .venv/bin/activate
python3 scripts/e2e_test.py
# 候补模式(定时轮询,有空即抢)
python3 scripts/api_standby.py --interval 10config_ui.py 在浏览器中打开配置页面,支持:
- 定时等待:设定抢场时刻(如 16:00),程序到点自动启动
- 时间窗口:偏好开始/结束时间
- 日期:今天 / 明天
- 优先级模式:quality(不靠墙优先)/ guarantee(靠墙保底)
- 靠墙场地等级:W1 ~ W4 分级
Playwright 打开 Chromium → CAS 统一认证登录
→ 缓存 token(5 小时有效)
→ API 查场地(~200ms)
→ 生成 Top-N 备选列表(按偏好排序)
→ 逐个尝试预约(遇冲突自动切换下一个备选)
→ 成功 / 全部失败
| 错误码 | 含义 | 自动处理 |
|---|---|---|
| 1610001 | 服务端限流 | 同场地重试最多 5 次 |
| 40100068 | 预约人数过多 | 同场地重试 |
| 40100077 | 已被他人抢占 | 立即切换下一个备选 |
Kotlin + Jetpack Compose 原生 Android 应用,与 API 直连方案共享同一套后端接口,通过 WebView 完成 CAS 登录后在手机上完成抢场。
- 语言:Kotlin 1.9
- UI:Jetpack Compose + Material 3
- 网络:OkHttp 4 + Gson
- 架构:MVVM(ViewModel + StateFlow)
- 最低 SDK:26 (Android 8.0)
- SharedWebView:共享一个 WebView 实例完成 CAS 登录,登录状态跨页面复用
- Token 缓存:自动缓存认证 token,过期自动重新登录
- 可视化抢场:场地网格、进度条、呼吸动画、烟花庆祝效果
- 通知提醒:抢场结果通过系统通知即时推送
- 配置界面:图形化设置所有抢场参数
- 过期机制:编译时设定过期日期
用 Android Studio 打开 android_app/ 目录,Sync Gradle 后直接 Run。
android_app/app/src/main/java/com/badminton/booking/
├── MainActivity.kt # 入口 Activity,过期检查
├── BadmintonApp.kt # Application 类
├── SharedWebView.kt # 全局共享 WebView(CAS 登录复用)
├── api/
│ └── BookingClient.kt # API 客户端(查场、预约、支付)
├── auth/
│ ├── AuthManager.kt # 认证管理(Token、Cookie)
│ └── TokenCache.kt # Token 持久化缓存
├── config/
│ └── AppConfig.kt # 应用配置
├── notify/
│ └── NotificationHelper.kt # 系统通知
├── ui/
│ ├── screen/ # 页面(Login、Config、Booking 等)
│ └── theme/ # Material 3 主题
└── viewmodel/
└── AppViewModel.kt # 状态管理
设置环境变量后,抢场启动、运行状态、成功/失败结果均自动推送至钉钉群:
export DINGTALK_WEBHOOK='https://oapi.dingtalk.com/robot/send?access_token=...'
export DINGTALK_SECRET='SEC...' # 可选,若机器人开启了加签未设置 Webhook 则仅终端输出,不影响程序运行。
.
├── config_ui.py # 抢场配置界面(浏览器,统一入口)
├── dingtalk_notify.py # 钉钉通知模块(共享)
├── config.json # 抢场参数(由 config_ui 写入)
├── credentials.json # 登录凭据(需自行创建,不提交)
│
├── safari_booking/ # Safari 自动点击方案
│ ├── .venv/
│ ├── scripts/
│ │ ├── check_court_availability.py # Safari 自动点击主脚本
│ │ ├── launcher.py # Safari 自动登录启动器
│ │ ├── standby_court.py # Safari 候补模式
│ │ └── standby_ui.py # 候补配置界面(浏览器)
│ └── snapshots/ # 调试 HTML 快照
│
├── api_reverse/ # API 逆向 + 直连方案
│ ├── .venv/
│ ├── scripts/
│ │ ├── e2e_test.py # API 抢场主程序
│ │ ├── api_standby.py # API 候补程序
│ │ ├── booking_client.py # API 客户端
│ │ ├── playwright_login.py # Playwright CAS 登录
│ │ ├── cas_auth.py # CAS 认证
│ │ └── sign.py # 接口签名算法
│ ├── docs/ # API 逆向文档
│ ├── har/ # HTTP Archive 抓包记录
│ └── js_source/ # 前端 JS 源码(逆向参考)
│
├── android_app/ # Android App
│
├── mobile_app/ # Mobile App(开发中)
│
└── docs/ # 文档
- 凭据安全:
credentials.json、.env、LoginScreen.kt等包含敏感信息,已加入.gitignore,请勿提交到仓库 - Token 有效期:API 方案中登录 Token 约 5 小时过期,程序会自动重新登录
- 代理:如果系统开着 ClashX,API 方案会自动使用
127.0.0.1:7890;校园网环境直连即可 - 合法使用:本工具仅供个人合理使用,请遵守场馆管理规定
MIT License — 仅供个人学习研究使用。