史上最“腦殘”的“搶火車票”程序(node.js版)


【背景】

快過年了,我媽一個電話打過來叫我給他買火車票,我到12306一查,硬座和硬卧基本沒有了,高鐵又太貴.

最后只搶了3張無座票,但是我媽說能不能買有座位的啊,我說沒有了啊,我媽:你過兩天再幫我看看。我:...

為了幫老媽搶到有座的票,后來用了360搶票插件,還用了網上的一個別人用c#寫的客戶端來搶票,媽的,用了兩三天都沒用。

最后還是打算自己用node寫一個,當時我的想法就是寫個簡單的,能用就行。

所以,思路如下:

用node寫一個爬蟲,每過一分鍾就爬取12306,查詢某一輛火車是否還有余票,有余票就給我發一封郵件,提醒我有余票了,然后我立馬登錄12306改簽。

這個思路的有兩點前提,第一、要自己提前確定好想買哪一輛火車,包括:火車車次,日期。第二、自己要經常在電腦前,只要一來郵件就去12306買票,這對於程序猿來說已經滿足了。

【代碼實現】

要想實現我的想法,運用到了2個node庫:nodemailernode-schedule,分別實現郵件和定時執行功能。

因為12306是https協議的,所以node的http模塊還是不行,這里可以用node的https模塊。

當然12306還需要有瀏覽器證書,我代碼里已經有了,大家下下來就可以用。

代碼:

 

var https = require('https');
var fs = require('fs');
var ca = fs.readFileSync('./cert/srca.cer.pem');
var nodemailer = require('nodemailer');
var schedule = require('node-schedule');
var config = {
    time:'2017-01-21',//日期格式必須是這樣
    from_station:'BJP',//始發站車站代碼,這里是北京北
    end_station:'XMS',//廈門
    train_num:'K571'//車次
    your_mail:'****@163.com',//你自己的郵箱,我這里用的是163郵箱,如果你要改其他類型的郵箱的話,那請你修改transporter里的服務器信息
    mail_pass:'****'//放心寫吧
};
var yz_temp = '',yw_temp = '';//保存余票狀態
function queryTickets(config){
    var options = { 
        hostname: 'kyfw.12306.cn',//12306
        path: '/otn/leftTicket/queryA?leftTicketDTO.train_date='+config.time+'&leftTicketDTO.from_station='+config.from_station+'&leftTicketDTO.to_station='+config.end_station+'&purpose_codes=ADULT',
        ca:[ca]//證書
    };
    var req = https.get(options, function(res){ 
    var data = '';
    var transporter = nodemailer.createTransport({
        host: "smtp.163.com",//郵箱的服務器地址,如果你要換其他類型郵箱(如QQ)的話,你要去找他們對應的服務器,
        secureConnection: true,
        port:465,//端口,這些都是163給定的,自己到網上查163郵箱的服務器信息
        auth: {
            user: config.your_mail,//郵箱賬號
            pass: config.mail_pass,//郵箱密碼
        }
    });
    res.on('data',function(buff){
        data += buff;//查詢結果(JSON格式)
    }); 
    res.on('end',function(){
        // console.log('res',data);
        var jsonData = JSON.parse(data).data;
        for(var i=0;i<jsonData.length;i++){
            var cur = jsonData[i];
            if(cur.queryLeftNewDTO.station_train_code==config.train_num){
                // console.log(cur);
                var yz = cur.queryLeftNewDTO.yz_num;//硬座數目
                var yw = cur.queryLeftNewDTO.yw_num;//硬卧數目
                var trainNum = cur.queryLeftNewDTO.station_train_code;//車次
                console.log('硬座',yz);
                console.log('硬卧',yw);
                if(yz!='無'&&yz!='--'||yw!='無'&&yw!='--'){
                    if(yw_temp == yw && yz_temp == yz){//當余票狀態發生改變的時候就不發送郵件
                        console.log('狀態沒改變,不重復發郵件');
                        return;
                    }
                    var mailOptions = {
                        from: config.your_mail, // 發件郵箱地址
                        to: config.your_mail, // 收件郵箱地址,可以和發件郵箱一樣
                        subject: trainNum+'有票啦,硬座:'+yz+',硬卧:'+yw, // 郵件標題
                        text: trainNum+'有票啦\n'+'時間是'+cur.queryLeftNewDTO.start_train_date+',\n出發時間:'+cur.queryLeftNewDTO.start_time+',\n到達時間:'+cur.queryLeftNewDTO.arrive_time+',\n歷時:'+cur.queryLeftNewDTO.lishi+',\n始發站:'+cur.queryLeftNewDTO.from_station_name+',\n到達:'+cur.queryLeftNewDTO.to_station_name, // 郵件內容
                    };

                    // 發郵件部分
                    transporter.sendMail(mailOptions, function(error, info){
                        if(error){
                            return console.log(error);
                        }
                        console.log('Message sent: ' + info.response);
                        yw_temp = yw;//保存當前列車的余票數量
                        yz_temp = yz;
                    });
                }else{
                    console.log('硬座/硬卧無票');
                }
                
                break;
            }
        }
        // fs.writeFile('./train.json',data);
    })  
});

req.on('error', function(err){
    console.error(err.code);
});
}
var rule = new schedule.RecurrenceRule();  
rule.second = [0];
schedule.scheduleJob(rule, function(){
        queryTickets(config);
        console.log('scheduleCronstyle:' + new Date());
}); 

 

下面說下上述代碼中的config里面的參數如何找到:

譬如我要找北京到廈門的火車:

首先進入12306余票查詢頁面:

 

點擊查詢之后控制台出現以下信息:

 

看最后一個點擊打開:

 

看到紅框里的內容就是config里面需要配置的選項了。

 

然后運行node main.js,然后一直放在那運行(可以放到自己的服務器上去運行)

運行結果:

 

總結一下,我這個如果想用這個買票,你只要配置config,替換里面的郵箱和密碼(你自己的郵箱),這樣就會收到郵件通知了。

目前我已經用這個把之前買的3張無座全都改簽為硬座票了(因為有人要退票啊,哈哈)

大家最好用163郵箱和163的手機客戶端吧,通知及時,一有郵件我的手機就會震動提示。

【更新於2017-4-1】

已經解決不能請求成功導致查詢不到余票信息的問題,同時修改了若干錯誤,現在已經可以正常使用。

現在的運行結果:

查詢結果:

 

代碼地址:node_12306

(希望大牛勿噴,多多指點,有空會完善功能。)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM