12306 订票助手 – 火车票自动订票 Chrome 扩展
快过年了,相信大家还能回想到往年春运时抢票的恐惧。要是热门线路,你想要买票简直就是难上加难。如果想要购票只能使用抢票软件,但是这些软件不仅需要购买加速包、找人助力,甚至可能导致隐私泄露,到最后抢票速度可能还不如自己手动来得快。于是周末便撸了这款插件:教大家如开发一个chrome插件,实现刷票购买、提交、确认一气呵成。
源码下载:https://gitee.com/leolee18/chrome-plugin12306
订票助手实现的功能
1、自动刷票,自动到购买界面。可以设置指定车次、席别固定为硬座、硬卧、二等座。
2、自动选定乘车人。
3、自动提交订单。
4、自动确认。
5、设置界面,可以设置车次、自动步骤、乘车人序号。
实现思路
通过监听网页加载完成和车票查询接口响应,触发对车票预订按钮的遍历。遍历到符合要求的预订按钮后,模拟点击预订按钮。如果没查到预订按钮,模拟点击查询按钮,则继续监听。如此循环直到可以预订。在购买页面,监听页面加载完成,触发对乘车人勾选。后面提交订单,由于12306对自动提交的订单,弹出的确认加了延时。所以我循环监听确认按钮的可用样式。可用后,自动提交确认。
设置界面。通过在车次票查询界面,注入一块设置框,实现用户设置车次、自动步骤、乘车人序号。车次是通过遍历界面上的车次表格来拿到的。为了方便下次打开使用,将设置信息保存在本地。
新建配置文件 manifest.json
//首先基础工作就是定义一个manifest.json (清单文件),用于定义插件相关的配置。
{
"manifest_version": 3, //指定您的应用包要求的清单文件格式的版本。
"name": "Chrome抢票",
"version": "1.0",
"description": "chrome抢票扩展,仅供技术交流",
"action": { //如果有 action, 即在chrome toolbar 的右边添加了一个 icon
"default_icon": {
"19":"img/icon.png"
}
},
"icons":{ //可定义一个或多个, 应用或主题背景的图标
"16": "img/icon.png",
"48": "img/icon.png"
},
"permissions": [//扩展使用的一些权限
"webRequest",
"tabs"
],
"host_permissions": [//主机权限v3版本,从permissions从分离出来了
"<all_urls>"
],
"background": {//后台网页,应用通常需要有一个长时间运行的脚本来管理一些任务或状态,而后台网页就是为这一目的而设立。
"service_worker": "background.js"
},
"content_scripts": [
{
"matches": ["https://kyfw.12306.cn/otn/leftTicket/init*"],// 匹配的地址网页
"js": ["js/data.js","js/scriptList.js"],// 内容脚本
"css":["css/index.css"]// 在页面上添加的css样式
},
{
"matches": ["https://kyfw.12306.cn/otn/confirmPassenger/initDc"],
"js": ["js/data.js","js/scriptSubmit.js"]
}
]
}
12306 查询界面开发
data.js 保存设置的数据
var mListAll = []; // 用户车次列表
var mAutos = []; // 自动提交的一些设置
var mInputs = []; // 用户输入乘车人序号
background.js 查询接口监听
var mBool = false; //限制加载完成后的口返回,避免重复加载数据
// 监听请求
chrome.webRequest.onHeadersReceived.addListener(function (details) {
// 判断是否是查询接口
if(details.url.indexOf('https://kyfw.12306.cn/otn/leftTicket/queryE') !== -1){
if(mBool){
// 发消息到content_scripts界面
chrome.tabs.query({active:true,currentWindow:true},function(tabs){
chrome.tabs.sendMessage(tabs[0].id,{name:'list',content:details.url});
});
}
}
return {responseHeaders: details.responseHeaders};
}, {
urls: [
"<all_urls>",// 监听所有请求
]
}, ['responseHeaders']
);
// 监听界面加载完成消息
chrome.runtime.onMessage.addListener(function(message,sender,sendResponse){
if(message.name == 'load'){
mBool = true;
}
});
scriptList.js 查询界面注入的开发
//筛选购买方法
function startButtonClick() {
// 所有可用的,预定按钮
var buttons = document.getElementsByClassName('btn72');
// 车次席别选择
var gt2dz = document.getElementById('cc_seat_type_O_check');
gt2dz?.click(); // 高铁二等座
var kyw = document.getElementById('cc_seat_type_3_check');
kyw?.click(); // 硬卧
var kyz = document.getElementById('cc_seat_type_1_check');
kyz?.click(); // 硬座
var mBool = false;
for (let index = 0; index < buttons.length; index++) {
var button = buttons[index];
if (button!='') {
var mHCNumber = button.parentNode.parentNode.childNodes[0].childNodes[0].childNodes[0].childNodes[0].childNodes[0]?.innerHTML;
if(mListAll.length == 0 || (mListAll.length > 0 && mListAll.includes(mHCNumber))){
// 自动触发预定按钮,去购买页面
if (mAutos.includes('zdcx')) {
button?.click();
mBool = true;
}
break;
}
}
}
if(!mBool){
//延时1妙刷新
setTimeout(function() {
// window.location.reload(); // 刷新页面,会慢一些
// 触发查询按钮,刷新
var inquireB = document.getElementById('query_ticket');
if (mAutos.includes('zdcx')) {
inquireB?.click();
}
}, 1000);
}
}
window.onload = function() {
// 去筛选购买
startButtonClick();
//发消息给后台网页,监听接口
chrome.runtime.sendMessage({
name: 'load',
content:''
});
}
// 监听后台网页口加载完成,也就是点查询按钮时,触发筛选购买。
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
if (message.name === "list") {
setTimeout(function() {
startButtonClick();
}
,50);
}
});
设置功能
window.onload = function() {
//设置界面
mIntLoad();//获取本地缓存
uiBody();//设置界面注入
//……
}
// 获取本地缓存数据实现
function mIntLoad() {
var mAll = localStorage.getItem('mListAll');
var mAut = localStorage.getItem('mAutos');
var mInp = localStorage.getItem('mInputs');
if(mAll){
mListAll = JSON.parse(mAll);
}else{
mListAll = [];
}
if(mAut){
mAutos = JSON.parse(mAut);
}else{
mAutos = [];
}
if(mInp){
mInputs = JSON.parse(mInp);
}else{
mInputs = [];
}
}
//设置界面注入实现,监听方法,本地存储等。大家看的懂,我就不废话了。
function uiBody() {
var tableList = document.getElementById('queryLeftTable');
var rowCount = tableList.rows.length;
var mCCDiv = document.createElement("div");
mCCDiv.className = "plugin-one";
for (let index = 0; index < rowCount; index++) {
var firstCellRowSpan = tableList.rows[index].cells.length;
if(firstCellRowSpan > 1){
var mCZDom = tableList.rows[index].cells[0].childNodes[0];
var mCZNumber = mCZDom.childNodes[0].childNodes[0].childNodes[0]?.innerHTML;
mCCDiv.appendChild(crateCheck(mCZNumber,mCZNumber,index,handleCheckboxChange));
}
}
…………
mPluDiv.appendChild(mAllDome);
var body = document.body;
body.appendChild(mPluDiv);
}
12306 订票界面开发
// 界面加载完成
window.onload = function() {
mIntLoad();//获取本地用户设置的数据。
sendButton();//界面功能注入
}
//界面功能注入
function sendButton() {
//设置乘车用户
if(Array.isArray(mInputs)){
mInputs.forEach((item)=>{
var label = document.getElementsByClassName('check')[item];
label.click();
})
}else{
var label = document.getElementsByClassName('check')[0];
label.click();
}
//触发提交按钮
var button = document.getElementById('submitOrder_id');
if (mAutos.includes('zdtj')) {
button.click();
}
// 监听界面弹框,有确认按钮时自动提交
loopSubmit();
}
//确认按钮自动提交实现,由于12306对自动提交后的确认按钮进行了限制,所以需要循环监听确认按钮的className变化。
function loopSubmit() {
var sendB = document.getElementById('qr_submit_id');
if(sendB){
if(sendB.className == 'btn92s'){
if (mAutos.includes('zdqr')) {
sendB.click();
}
}else{
sendB.className = 'btn92s';
setTimeout(loopSubmit, 500);
}
}else{
setTimeout(loopSubmit, 30);
}
}
安装
1、打开google chrome的菜单(三条线图标)
2、点击”更多工具“ > ”扩展程序“
3、打开开发者模式
4、点击”加载已解压的扩展程序“,选择文件目录
5、点击”启用“即可使用