插件实现12306网站“按预填信息”自动抢票

简介: 年底抢票回家过年,12306的“按预填信息购票”功能却隐藏按钮,导致抢票困难。程序员发现按钮被CSS隐藏后尝试手动修改,但遇到自动恢复和确认窗口的问题。最终决定开发Chrome插件,通过监听页面按钮自动点击,实现一键抢票。该插件结构简单,包含manifest.json、popup.html等文件,能有效节省抢票时间。代码已开源至GitHub,供有需要的人参考使用。

抢票那些事

年底了,为了回老家过年,大部分同学都需要抢票,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”,我猜应该是有一个脚本在维护这一块的隐藏。

当我以为等着页面上的倒计时结束,“按预填信息购票”按钮出现后,立马点击一下就能开始抢票,然而并不是,当时的我心中真的是一万匹**马在崩腾。点击“按预填信息购票”按钮后,竟然还会弹出购票信息确认窗口,需要我们再次点击“提交订单”按钮。

Snipaste_2025-01-10_22-52-24.png

好家伙,这还抢个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”按钮,脚本就会在当前活动页面上执行,可以打开浏览器控制台看日志。

演示使用

利用我们前面写的模拟页面,来演示一下插件的使用:

show2.gif

显然,我们开发的这个插件非常的简陋,还有报错信息没有处理,另外每隔5毫秒检查一次DOM元素对浏览器性能有一定影响,不过足以满足我们的自动化需求。

另外,在使用插件之前,我们需要注意几点:

  • 更新主机的系统时间,12306上面的倒计时貌似是根据系统时间来的,如果你的系统时间慢了,结果可想而知

    mac上可以执行命令:sudo ntpdate -u time.apple.com

  • 在倒计时还有两分钟的时候开启我们这个插件功能,避免长时间高频检查DOM元素导致对浏览器性能产生影响

结束语

平时我都是拿来主义,想要什么插件就在chrome的插件市场或者tampermonkey里找,大部分都是能找到的。第一次硬着头皮自己捣鼓了一个,虽然过程中遇到了好几个坎,功能也很简陋,但最终到达了目标,节约了抢票时间,心里还是美滋滋的。所以把这次经历记录下来,希望对大家有一丢丢帮助。

上述所有代码都已经放在了github,需要的自取哦。

相关文章
|
传感器 监控 Java
如何正确理解 CPU 使用率和平均负载的关系?看完你就知道了
CPU(Central Processing Unit)是计算机系统的运算和控制核心,是信息处理、程序运行的最终执行单元,相当于系统的“大脑”。
3859 0
如何正确理解 CPU 使用率和平均负载的关系?看完你就知道了
|
9月前
|
存储 前端开发 Java
Harry技术添加存储(minio、aliyun oss)、短信sms(aliyun、模拟)、邮件发送等功能
### SpringBoot3 + Vue3 前后端分离的Java快速开发框架更新 本次更新主要包含以下内容: 1. **端口修改**:为避免与Minio存储服务冲突,后端启动端口从9000改为9999。 2. **添加存储支持**:集成Minio和阿里云OSS对象存储服务,详细配置请参考相关文档。 3. **短信服务**:接入阿里云短信服务,并增加模拟发送功能,方便本地测试。 4. **邮件发送**:引入邮件发送功能,支持简单文本邮件和带附件邮件。 5. **完善个人中心**:优化个人中心页面,提升用户体验。
329 85
Harry技术添加存储(minio、aliyun oss)、短信sms(aliyun、模拟)、邮件发送等功能
|
9月前
|
存储 人工智能 自然语言处理
|
9月前
|
SQL 存储 开发框架
ASPX+MSSQL注如
SQL注入是一种严重的安全漏洞,可能导致敏感数据泄露或被恶意操控。通过使用参数化查询、存储过程、ORM框架和输入验证等方法,可以有效地防止SQL注入攻击。定期进行代码审计和安全测试,确保应用程序的安全性。
104 34
|
9月前
|
Java 开发者 Spring
java springboot监听事件和处理事件
通过上述步骤,开发者可以在Spring Boot项目中轻松实现事件的发布和监听。事件机制不仅解耦了业务逻辑,还提高了系统的可维护性和扩展性。掌握这一技术,可以显著提升开发效率和代码质量。
216 33
|
9月前
|
人工智能 监控 API
体验《多模态数据信息提取》
体验《多模态数据信息提取》
|
9月前
|
SQL 算法 Java
从集合运算设计 Lambda 语法
集合化程序语言通过简洁的语句实现复杂集合运算,Lambda语法的设计至关重要。首先,直接使用集合成员进行简单运算如求和,无需Lambda。接着,引入Lambda表达式处理更复杂的运算,如计算平方和。SPL使用“~”表示当前成员,简化表达,而SQL则依赖字段引用机制,牺牲了对泛型成员的支持。在嵌套引用时,采用就近原则避免歧义。SPL拓展了SQL的数据组织,支持任意成员的集合,使Lambda语法更加灵活。
|
9月前
|
人工智能 安全 搜索推荐
《盘古大模型——鸿蒙NEXT的智慧引擎》
华为HarmonyOS NEXT发布,将AI与操作系统深度融合,开启智能新时代。其中,盘古大模型为核心,赋予小艺智能助手超强的记忆、推理和规划能力,支持23类记忆类型及万亿token知识量,实现复杂功能如图片转表格、邮件规划导航等,极大提升用户操作效率。同时,盘古大模型助力开发者快速开发智能应用,降低开发门槛,推动智能生态发展。系统还通过星盾安全架构保障数据安全与隐私,确保用户体验更智能、更安全。
733 18
|
自然语言处理 IDE 开发工具
通义灵码编程智能体上线,支持Qwen3模型
通义灵码最全使用指南,一键收藏。
128912 31
通义灵码编程智能体上线,支持Qwen3模型
|
Python
一个12306自动抢票的脚本
一个12306自动抢票的脚本
2580 1