【背景】
快过年了,我妈一个电话打过来叫我给他买火车票,我到 12306 一查,硬座和硬卧基本没有了,高铁又太贵.
最后只抢了 3 张无座票,但是我妈说能不能买有座位的啊,我说没有了啊,我妈:你过两天再帮我看看。我:...
为了帮老妈抢到有座的票,后来用了 360 抢票插件,还用了网上的一个别人用 c# 写的客户端来抢票,妈的,用了两三天都没用。
最后还是打算自己用 node 写一个,当时我的想法就是写个简单的,能用就行。
所以,思路如下:
用 node 写一个爬虫,每过一分钟就爬取 12306,查询某一辆火车是否还有余票,有余票就给我发一封邮件,提醒我有余票了,然后我立马登录 12306 改签。
这个思路的有两点前提,第一、要自己提前确定好想买哪一辆火车,包括:火车车次,日期。第二、自己要经常在电脑前,只要一来邮件就去 12306 买票,这对于程序猿来说已经满足了。
【代码实现】
要想实现我的想法,运用到了 2 个 node 库:和, 分别实现邮件和定时执行功能。
因为 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) {
- 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 的手机客户端吧,通知及时,一有邮件我的手机就会震动提示。
代码地址:
(希望大牛勿喷,多多指点,有空会完善功能。)
来源: