【背景】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
(但愿大牛勿喷,多多指点,有空会完善功能。)