插件实现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,需要的自取哦。

相关文章
|
8月前
|
小程序
小程序一直未提审的原因及解决方案
小程序一直未提审的原因及解决方案
103 11
|
8月前
|
Go
区域代理分红商城系统开发指南教程/步骤功能/方案逻辑/源码项目
The development of regional proxy dividend distribution mall system involves multiple aspects such as proxy dividend function and electronic mall system development. The following is an overview of the steps for developing a regional agent dividend distribution mall system
|
Python
一个12306自动抢票的脚本
一个12306自动抢票的脚本
1348 1
|
SQL 前端开发 Java
校园外卖点餐系统——Day01【项目简介、开发环境搭建、后台系统登录和退出功能】
校园外卖点餐系统——Day01【项目简介、开发环境搭建、后台系统登录和退出功能】
211 0
校园外卖点餐系统——Day01【项目简介、开发环境搭建、后台系统登录和退出功能】
|
小程序 前端开发 Java
代小程序实现业务开发,99%还原公众号后台对服务类目管理的功能
关于微信开放平台上的代小程序实现业务,之前就写了相关模块的代码,包括快速注册小程序、代码上传、提交审核、发布小程序等主要功能。
130 0
代小程序实现业务开发,99%还原公众号后台对服务类目管理的功能
|
JavaScript 前端开发
网站添加本站已稳定运行XX天的统计代码
网站添加本站已稳定运行XX天的统计代码
412 0
网站添加本站已稳定运行XX天的统计代码
|
JavaScript Unix Linux
PHP开发的网站,如何实现批量打印快递单的功能?
虽然市场中不断有新的编程语言诞生,但不得不承认PHP在web开发领域仍一直占有很大份额,高效开发是他一直以来最大的一个优点,而且他的学习成本也较低,入门门槛较低,和Python一样是解释性语言,很多人都说他是世界上最好的语言。其实语言并没有好坏之分,只有是否适用,每门语言都有自己的优缺点,毕竟想要获得一样东西,总要放弃点什么,有舍有得,看自己怎么权衡,只要能解决问题就是好的,不管黑猫白猫抓到老鼠就是好猫。自从swoole的加持以及和GO语言的结合,也弥补语言本身的很多不足,慢慢缩小和其他语言之间的差距,不过还是有很长的路要走。--题外话
389 0
|
前端开发 API PHP
MyCms 自媒体 CMS 系统 v2.5,后台一键升级更新
MyCms是一款基于Laravel开发的开源免费的自媒体博客CMS系统,助力开发者知识技能变现。
175 0
MyCms 自媒体 CMS 系统 v2.5,后台一键升级更新
|
前端开发 UED
悬赏任务源码,了解更多加载方式提升用户体验
悬赏任务源码,了解更多加载方式提升用户体验