【背景】node
快過年了,我媽一個電話打過來叫我給他買火車票,我到12306一查,硬座和硬臥基本沒有了,高鐵又太貴.git
最後只搶了3張無座票,可是我媽說能不能買有座位的啊,我說沒有了啊,我媽:你過兩天再幫我看看。我:...github
爲了幫老媽搶到有座的票,後來用了360搶票插件,還用了網上的一個別人用c#寫的客戶端來搶票,媽的,用了兩三天都沒用。json
最後仍是打算本身用node寫一個,當時個人想法就是寫個簡單的,能用就行。c#
因此,思路以下:瀏覽器
用node寫一個爬蟲,每過一分鐘就爬取12306,查詢某一輛火車是否還有餘票,有餘票就給我發一封郵件,提醒我有餘票了,而後我立馬登陸12306改簽。服務器
這個思路的有兩點前提,第1、要本身提早肯定好想買哪一輛火車,包括:火車車次,日期。第2、本身要常常在電腦前,只要一來郵件就去12306買票,這對於程序猿來講已經知足了。ui
【代碼實現】spa
要想實現個人想法,運用到了2個node庫:nodemailer和node-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
(但願大牛勿噴,多多指點,有空會完善功能。)