学校图书馆换了新的验证码预约系统,用户每天22:45准时完成滑块验证才能抢到次日座位,整个流程包括登录系统→选择区域→对准滑块缺口→卡点提交
,任何一个环节延迟都可能前功尽弃,可谓手残党克星。当我第三次因为手抖把滑块滑偏,看着近10秒的验证耗时,意识到必须想办法自救。从灵光乍现到方案成型两杯咖啡的时间,以下是一点实战心得。
🥳 最终实现效果
Before: 手动模式
"bookDate": "2025-xx-xx 22:45:04",
"bookDate": "2025-xx-xx 22:45:07",
"bookDate": "2025-xx-xx 22:45:05"
After: Tampermonkey 脚本
"bookDate": "2025-xx-xx 22:45:00",
目前方案虽不算完美(仍需15秒内完成手动验证),但实测将操作误差从±7秒压缩到毫秒级,已经能保证稳定抢到目标座位。就像给老式汽车加装定速巡航——虽不是全自动驾驶,但至少不用再脚踩油门与时间赛跑~
1️⃣ 原始手动模式
滑动滑块到缺口位置,然后在22:45:00
迅速松开。最简单直观的方法。但这个方法最大的敌人是薛定谔的验证时间:你以为4秒能搞定,实际可能花7秒。最崩溃的一次尝试:22:44:59
松手,“未到预约开放时间”;22:45:00
松手,“验证码被黑洞吸走了,请重试”。可能遇到的情况:22:45:03
重试成功,心仪座位已被约。
虽然实际因为运气好,暂时还没遇到没抢到座位的情况,但通常目标座位区域在15秒内就已经全部被抢光,所以这个方法的容错率很低。在“可能抢不到”的焦虑催生下,有了后续的方案。
2️⃣ Proxyman 断点:延迟执行冻结请求
由于预约请求 /bookseat
是在验证码校验 /check
成功后立即POST,那么如果能控制/bookseat
请求发送的精确时间,就能绕过不确定的人类操作延迟。选择Proxyman是因为它的 Breakpoint 功能可以冻结请求,就像给网络请求打了断点,可以手动延迟执行请求。
设置:在 Proxyman Tools > Breakpoints
中设置断点规则
Matching Rule (Request): https://xxxx.xxx.xxx.edu.cn/*/bookseat/*
预约步骤:
- 确保其他 Proxy 工具已关闭(必须)。
- 打开已启用 SSL proxying 的浏览器(例如 Chrome),并选择座位。
- 点击“确认”按钮,调起滑块验证,并在
22:44:45
秒后通过验证,自动唤起Proxyman 断点窗口。 - 在
22:45:00
秒,在断点窗口 Excute Request。
这样,在 Proxyman 中设置 Breakpoint Rule 后,当请求匹配到指定的 URL时,会自动暂停请求并在断点窗口中显示。用户可以在断点窗口中手动延迟请求的执行时间,以便在指定的时间点(22:45:00
秒)执行 Request,完成抢座位的操作。
实测发现关键点在于:
- 系统的timeout设置约15秒,所以最多提前15秒完成滑块验证
- 本地时间应该要与服务器严格同步(这个误差目前可以忽略)
- Proxyman的Proxy和电脑其他Proxies不能兼容,setup起来比较麻烦
3️⃣ Proxyman 脚本:15秒容错窗口
但是,每次都盯着时钟点"Execute"请求还是有点麻烦。于是,我想到用 proxyman scripting 直接让程序自动处理。
设置:在
Proxyman > Scripting
中设置下面的脚本// # Name: 捕获并延迟到指定时间请求
// # URL (Request): https://xxxx.xxx.xxx.edu.cn/*/bookseat/*
// 图书馆预约开放时间
const TARGET_HOUR = 22; // 小时(24小时制)
const TARGET_MIN = 45; // 分钟
const TARGET_SEC = 00; // 秒
function onRequest(context, url, request) {
// 计算目标时间
const now = new Date();
const targetTime = new Date(
now.getFullYear(),
now.getMonth(),
now.getDate(),
TARGET_HOUR,
TARGET_MIN,
TARGET_SEC
);
// 计算延迟时间(毫秒)
const delay = targetTime - now;
// 时间窗口检查(0-15秒)
if (delay >= 0 && delay <= 15000) {
console.log(`延迟请求: ${delay}ms`);
sleep(delay);
return request;
} else {
console.log("放弃请求:超出时间窗口");
return request;
}
}
预约步骤:
- 确保其他 Proxy 工具已关闭(必须)。
- 打开已启用 SSL proxying 的浏览器(例如 Chrome),并选择座位。
- 调起滑块验证,并在
22:44:45
秒后通过验证,自动唤起Proxyman 断点窗口。
这样,在 Proxyman 中设置 Script 后,当请求匹配到指定的 URL,脚本会延迟到22:45:00
准时执行请求,从而提高抢座位的成功率。
4️⃣✅ 完全基于浏览器方案:Javascript Bookmarklet
那么,既然能直接拦截浏览器XHR请求,为什么不省去设置外部代理工具的环境,完全基于浏览器脚本实现?可以在 Chrome 的 Tampermonkey Extension (仅支持 PC 端,IOS 端的 Chrome 暂时不支持 Extension) ,或在 Safari 添加 Javascript Bookmarklet书签 (支持 Mac 和 IOS端) 运行下面的 script。
其中,Bookmarklet 将脚本压缩为单行 URI 的形式(以 javascript:();
嵌套脚本内容),保存为 Safari 书签后,需要在目标页面加载后手动点击书签执行脚本。
Script
// ==UserScript==
// @name 学校图书馆预约抢座(22:44:45~45:00完成滑块验证)
// @namespace http://tampermonkey.net/
// @version 0.1
// @description 捕获并延迟POST请求到指定时间
// @author Tiana Lei
// @match https://xxxx.xxx.xxx.edu.cn/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
const TARGET_HOUR = 22; // 小时(24小时制)
const TARGET_MIN = 45; // 分钟
const TARGET_SEC = 0; // 秒
var originalOpen = XMLHttpRequest.prototype.open;
var originalSend = XMLHttpRequest.prototype.send;
// 正则表达式,用于匹配URL
const targetPattern = /^https:\/\/xxxx\.xxx\.xxx\.edu\.cn\/.*bookseat\/.*/;
XMLHttpRequest.prototype.open = function(method, url) {
if (method === "POST" && targetPattern.test(url)) {
var that = this;
this.realSend = this.send; // 保存原始send方法
this.send = function(data) {
// 获取当前时间和目标时间
const now = new Date();
const targetTime = new Date(
now.getFullYear(),
now.getMonth(),
now.getDate(),
TARGET_HOUR,
TARGET_MIN,
TARGET_SEC
);
// 计算延迟时间(毫秒)
const delay = targetTime - now;
console.log(`当前时间:${now}, 目标时间:${targetTime}, 计算得到延迟:${delay}ms`); // 输出延迟时间
// 延迟发送请求
if (delay > 0 && delay <= 15000) { // 如果在时间窗口内
console.log(`延迟发送:${delay}ms`); // 输出延迟信息
setTimeout(function() {
console.log('延迟请求被发送'); // 延迟后执行的日志
that.realSend(data); // 延迟发送原始请求
}, delay);
} else {
console.log('超出时间窗口,直接发送请求'); // 如果超出时间窗口,直接发送请求
that.realSend(data); // 超出时间窗口,直接发送
}
};
} else {
console.log(`跳过,非目标POST请求,URL: ${url}`); // 如果不是目标请求,跳过
}
originalOpen.apply(this, arguments); // 调用原始open方法
};
XMLHttpRequest.prototype.send = function(data) {
originalSend.apply(this, arguments); // 调用原始send方法
};
})()
现在的最佳操作流程变成:
22:44:30
优雅地打开页面22:44:45
~22:44:59
从容完成滑块验证(预留15秒容错)- 浏览器脚本在00秒准时发送请求
至此,抢座焦虑已成过去式。Enjoy ~