抢票那些事
年底了,为了回老家过年,大部分同学都需要抢票,12306提供了"按预填信息购票"的功能,先把购票信息填好,到点直接抢票,但是这个功能有点蛋疼,到点开票之前,页面上根本就看不到"按预填信息购票"按钮,只有页面的倒计时结束,用户才能看到这个按钮,检查源代码可以发现,开发人员把这个按钮给隐藏了。
<div class="ticket-btn" style="user-select: text;">
<a href="javascript:;" class="btn btn-hollow btn-sm w120 buy-ticket-button" data-index="0" data-seatno="3"
style="display: none; user-select: text;">按预填信息购票</a>
<span class="txt-lighter qishou-text" style="user-select: text;">1月9日09点
起售</span>
</div>
作为程序员的我,心里暗爽,“这不是小场面~”,把style="display: none; user-select: text;"改成style="display: inline-block; user-select: text;"不就行。说干就干,可是当我改成“display: inline-block”之后,发现立马又被改回了“display: none”,我猜应该是有一个脚本在维护这一块的隐藏。
当我以为等着页面上的倒计时结束,“按预填信息购票”按钮出现后,立马点击一下就能开始抢票,然而并不是,当时的我心中真的是一万匹**马在崩腾。点击“按预填信息购票”按钮后,竟然还会弹出购票信息确认窗口,需要我们再次点击“提交订单”按钮。
好家伙,这还抢个der,无奈抢票时间就这样一点点被浪费,最终喜提的“已售罄”三个大字像是在屏幕前嘲笑我:“你个菜鸡,没有抢到票吧”。
这我哪能忍!所以决定研究一下怎么搞一个google chrome插件,实现一键自动化,节省两次点击按钮的时间,毕竟抢票的时间是很宝贵的,哪怕节约哪怕1毫秒,可能我们就已经排在很多人前面了。
实现一个chrome插件
凭借着以前学过的一点点前端知识和几个小弟(文心一言、豆包、chatGPT),从没有搞过chrome插件开发的我,似乎有那么一点点出生牛犊不怕虎的意思,这就开始干了。
明确需求
首先根据场景,明确一下我们的需求。希望达到的效果是,通过chrome插件,自动监听页面上的按钮控件,一是当出现“按预填信息购票”按钮时,自动触发点击;二是当出现“提交订单”按钮时,自动触发点击。
功能实现
秉承着面向小弟编程的原则,经过一通通高手之间“对话”,再加上验证调试,最终达到了效果。下面记录一下实现过程,具体的原理就不讲了,因为我也不懂哈哈。
模式12306的页面
写一个页面,模拟12306页面上的从“1月9日09点 起售”到“按预填信息购票”按钮出现的过程,再模拟当用户点击“按预填信息购票”按钮时,弹出“购票信息确认”窗口,显示“提交订单”按钮。代码放在github仓库里,这里就不贴了
编写插件
1、创建 Chrome 扩展目录结构
首先,创建一个包含以下文件的目录结构:
├── 12306 pre-filled buy
│ ├── icons //插件图标
│ │ ├── icon128.png
│ │ ├── icon16.png
│ │ └── icon48.png
│ ├── manifest.json //配置文件
│ ├── popup.html //弹出界面
│ └── popup.js
2、编写manifest.json
{
"manifest_version": 3,
"name": "12306 按预填信息购票",
"version": "1.0",
"description": "12306 按预填信息购票",
"permissions": [
"activeTab",
"alarms",
"scripting"
],
"action": {
"default_popup": "popup.html",
"default_icon": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
},
"icons": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
}
3、编写popup.html
<!DOCTYPE html>
<html>
<head>
<title>12306 按预填信息购票</title>
<style>
body {
width: 200px;
height: 50px;
display: flex;
justify-content: center;
align-items: center;
background-color: #f1f1f1;
}
button {
padding: 10px 20px;
font-size: 16px;
}
</style>
</head>
<body>
<button id="goHome">I Must Go Home</button>
<script src="popup.js"></script>
</body>
</html>
4、编写popup.js
document.getElementById('goHome').addEventListener('click', function() {
// 首先获取当前活动标签页的ID
chrome.tabs.query({
active: true, currentWindow: true}, function(tabs) {
var tabId = tabs[0].id;
// 使用获取到的tabId来执行脚本
chrome.scripting.executeScript({
target: {
tabId: tabId},
func: buttonlistener
}, function() {
console.log('button listener enabled in tab:', tabId);
});
});
});
function buttonlistener() {
console.log('listening start...');
const intervalId0 = setInterval(() => {
const targetButtonSelector = 'a.buy-ticket-button[style*="display: inline-block;"]';
const targetButton = document.querySelector(targetButtonSelector);
if (targetButton && window.getComputedStyle(targetButton).display !== 'none') {
targetButton.click();
console.log('yushou, clicking...');
clearInterval(intervalId0); // 停止检查
}else{
console.log('Not found the yushou button, waiting...');
}
}, 3); // 每隔3毫秒检查一次
const intervalId = setInterval(() => {
const submitButton = document.querySelector('a[href="javascript:;"][class="btn btn-primary ok"]');
if (submitButton) {
submitButton.click();
console.log('Found the submit button, clicking...');
clearInterval(intervalId); // 停止检查
}else{
console.log('Not found the submit button, waiting...');
}
}, 5); // 每隔5毫秒检查一次
}
5、加载扩展
- 打开Chrome浏览器,进入
chrome://extensions/
。 - 打开右上角的“开发者模式”
- 点击“加载已解压的扩展程序”按钮,选择你的
12306 pre-filled buy
文件夹。
经过以上步骤后,应该就能在Chrome工具栏中看到你的扩展图标。点击图标,然后点击“I Must Go Home”按钮,脚本就会在当前活动页面上执行,可以打开浏览器控制台看日志。
演示使用
利用我们前面写的模拟页面,来演示一下插件的使用:
显然,我们开发的这个插件非常的简陋,还有报错信息没有处理,另外每隔5毫秒检查一次DOM元素对浏览器性能有一定影响,不过足以满足我们的自动化需求。
另外,在使用插件之前,我们需要注意几点:
更新主机的系统时间,12306上面的倒计时貌似是根据系统时间来的,如果你的系统时间慢了,结果可想而知
mac上可以执行命令:
sudo ntpdate -u time.apple.com
- 在倒计时还有两分钟的时候开启我们这个插件功能,避免长时间高频检查DOM元素导致对浏览器性能产生影响
结束语
平时我都是拿来主义,想要什么插件就在chrome的插件市场或者tampermonkey里找,大部分都是能找到的。第一次硬着头皮自己捣鼓了一个,虽然过程中遇到了好几个坎,功能也很简陋,但最终到达了目标,节约了抢票时间,心里还是美滋滋的。所以把这次经历记录下来,希望对大家有一丢丢帮助。
上述所有代码都已经放在了github,需要的自取哦。