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

【背景】node

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

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

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

最後仍是打算本身用node寫一個,當時個人想法就是寫個簡單的,能用就行。c#

因此,思路以下:瀏覽器

用node寫一個爬蟲,每過一分鐘就爬取12306,查詢某一輛火車是否還有餘票,有餘票就給我發一封郵件,提醒我有餘票了,而後我立馬登陸12306改簽。服務器

這個思路的有兩點前提,第1、要本身提早肯定好想買哪一輛火車,包括:火車車次,日期。第2、本身要常常在電腦前,只要一來郵件就去12306買票,這對於程序猿來講已經知足了。ui

【代碼實現】spa

要想實現個人想法,運用到了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

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

相關文章
相關標籤/搜索