chrome插件12306抢票

1,296 阅读5分钟

12306 订票助手 – 火车票自动订票 Chrome 扩展

快过年了,相信大家还能回想到往年春运时抢票的恐惧。要是热门线路,你想要买票简直就是难上加难。如果想要购票只能使用抢票软件,但是这些软件不仅需要购买加速包、找人助力,甚至可能导致隐私泄露,到最后抢票速度可能还不如自己手动来得快。于是周末便撸了这款插件:教大家如开发一个chrome插件,实现刷票购买、提交、确认一气呵成。

源码下载:https://gitee.com/leolee18/chrome-plugin12306

alt 效果图

订票助手实现的功能

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、点击”启用“即可使用