如何用Node.js编写高效抢票脚本?
- 内容介绍
- 文章标签
- 相关推荐
本文共计4156个文字,预计阅读时间需要17分钟。
在深圳加班是件非常辛苦的事情,尤其是我所在的科技创新园区,人非常多,每天上下班都像春运一样。如果能换到以前的大冲上班那就太幸福了,可惜换不了。
要知道在深圳上班是非常痛苦的事情,特别是我上班的科兴科技园这一块,去的人非常多,每天上班跟春运一样,如果我能换到以前的大冲上班那就幸福了,可惜,换不得。
尤其是我这个站等车的多的一笔,上班公交挤的不行,车满的时候只有少部分人能硬挤上去。通常我只会用两个字来形容这种人:“公交怪”
想当年我朋友瘦的像只猴还能上去,老子身高182体重72kg挤个公交,不成问题,反手一个阻挡,闷声发大财,前面的阿姨你快点阿姨,别磨磨唧唧的,快上去啊阿姨,嗯?你还想挤掉我?你能挤掉我?你能挤掉我!我当场!把车吃了!
....
咳咳,挤公交是不可能挤公交滴,因为今天我发现了一个可以定制路线的网约巴士公众号
但是呢,票经常会被抢光,同时我还我发现,有时候会有人退票,这时候就有空余票了,关键是我不可能时时都在公众号上盯着,于是,我就写了一个抢票+短信通知的小工具
获取接口信息
1.查看页面结构
这个就是订票页面,显示当前月的车票情况,根据图示,红色为已满,绿色为已购,灰色为不可选
如果是可选就是白色的小方块,并且在下面显示余票,如下图所示:
我们打算这么做,
- 定时抓取返回的接口信息
- 根据接口返回值判断是否有余票
好,审查下源代码看下接口信息,等等,微信浏览器没办法审查源代码,于是
2.使用chrome调试微信公众号网页页面
首先面临个问题,如果直接copy公众号网页Url在chrome打开的话,就会显示这个画面,他被302重定向到了这个页面,所以是行不通的,只有获取OAuth2.0授权才能进去
所以我们得先通过抓包工具,知道手机访问微信公众号网页的时候,需要带什么信息过去,这时候我们就得借助抓包工具,因为我电脑是Mac,用不了 Fiddler ,我用的是 Charles 花瓶,就是下面这位仁兄
借助这个工具,我们只需3步就可以轻松搞定手机数据抓包:
- 获取本机IP地址和端口
- 设置代理手机上网
- 依次执行上面两步
2-1 获取本机IP地址和端口
第一步,找到端口号,一般默认是8088,但是为了确认可以打开 Proxy / Proxy Setting 看下,哦原来我之前设置成了8888
然后找到 Charles 的 help / Local IP Address,点击它就会看到自己的本机地址,找到本机地址记下来,然后进行下一步
2-2设置代理手机上网
首先保证手机跟电脑连接的是同一个wifi,然后在wifi设置那里会有设置代理信息,比如我的猴米...不对,小米9手机!设置如下:
输入上一步获取主机名,端口号就ok了
输入完成,点击确定后。 Charles 就会弹出一个对话框,问你是否同意接入代理,点击确定allow就行了。
2-3 用手机访问目标网页
我们用手机访问微信公众号进入到抢票页面后,发现 Charles 已经成功抓包到了网页信息,当我们进入这个抢票页面的时候,他会发起两个请求,一个是获取document文档内容,一个post请求获取票务信息。
仔细分析了下,大概明白了业务逻辑:
整个项目技术站是java+jsp,传统写法,用户身份验证主要是cookie+session方案,前端这一块主要是使用 jQuery 。
当用户进入页面的时候,会携带查询参数,如起始站点,时间,车次等信息和cookie请求document文档, 也就是圈起来的这一块,
而我们想要的核心内容:日历表,一开始是不显示的
因为还要在请求一次
第二次请求,携带cookie和以上的查询参数发起一个post请求,获取当月的车票信息,也就是日历表内容
下面这个是请求当月票务信息,然而发现他返回的是一堆html节点
好吧...估计是获取到之后直接 append 到 div 里面的,然后渲染生成日历表内容
接着在手机上操作,选择两个日期,然后点击下单,发送购票请求,拉取购票接口,我们看下购票接口的请求和返回内容:
看下request 内容,根据字段的意思大概明白是线路,时间,以及车票金额,还有支付方式
在看看返回的内容:返回一个json字符串数据,里面大概涵盖了下单的成功返回码,时间,id号等等信息
2-4 记录所需要的信息内容
根据上面的分析,总结下内容: 整个项目用户身份验证是使用 cookie 和 session 方案,请求数据用的是 form data 方式,请求字段啥的我们也都清楚,唯独有一点,就是请求余票的时候,返回的是html节点代码,而不是我们预期的json数据,这样就有个麻烦,我们没办法一目了然的明白他余票的时候是如何显示的
所以我们只能通过 chrome 进行调试,才能得出他是如何判断余票的。
我们找个记事本,记录下信息,记录的内容有:
- 请求余票接口和购票接口的
url地址 cookie信息 各自的request参数字段user-Agent信息- 各自的
response返回内容
2-5 设置chrome
有以上信息后,我们就可以开始用chrome调试了, 首先打开 More tools / Network conditions
把 user-Agent 填入到 Custom里面
2-6 Charles抓包本地请求
因为我们要把获取到的cookie填入到chrome里面,以我们的用户身份去访问网页,所以我们需要在请求目标地址的时候,改包修改cookie
首先我们需要开启 macOS Proxy ,抓包我们的weixin.xxxx.net/ebus/front/wxQueryController.do?BcTicketCalendar', this.postData, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': "Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Mobile/12A365 MicroMessenger/5.4.1 NetType/WIFI",
"Cookie": this.cookie
}
})
}
handleBuyTicket(){} //购票相关逻辑
requestOrder(){}//调用购票接口
handleInfoUser(){}//通知用户的逻辑
sendMSg(){} //发短信接口
}
来解释下那行正则, cheerio 抓取到的dom是长这样的,第一个 span 内容是日期,第二个是余票数量
所以我们要把它格式化变成这种数组,也就是 ticketList
2-3开发购票功能
首先我们在 init 方法里做个判断,如果有余票才去购票,没有余票购个毛
class QueryTicket{ constructor({ data, phoneNumber, cookie, day }) { //constructor代码... } //初始化 async init(){ let ticketList = await this.handleQueryTicket() //如果有余票 if (ticketList.length) { //把余票传入购票逻辑方法,返回短信通知所需要的数据 let resParse = await this.handleBuyTicket(ticketList) } } //查询余票的逻辑 async handleQueryTicket(){ // 查询余票代码... } //调用查询余票接口 requestTicket(){ //调用查询余票接口代码... } //购票相关逻辑 async handleBuyTicket(ticketList){ let year = new Date().getFullYear() //年份, let month = new Date().getMonth() + 1 //月份,拼接购票日期用得上,因为余票接口只返回几号 let { onStationName,//起始站点名 offStationName,//结束站点名 lineId,//线路id vehTime,//发车时间 startTime,//预计上车时间 onStationId,//上车的站台id offStationId //到站的站台id } = this.data // 初始化的数据 let station = `${onStationName}-${offStationName}` //站点,发短信时候用到:"宝安交通局-深港产学研基地" let dateStr = ""; //车票日期 let tickAmount = "" //总张数 ticketList.forEach(item => { dateStr = dateStr + `${year}-${month}-${item.day},` tickAmount = tickAmount + `${item.ticketLeft}张,` }) var buyTicket = { lineId,//线路id vehTime,//发车时间 startTime,//预计上车时间 onStationId,//上车的站点id offStationId,//目标站点id tradePrice: '5', //金额 saleDates: dateStr.slice(0, -1), payType: '2' //支付方式,微信支付 } // 调用购票接口 let data = querystring.stringify(buyTicket) let res = await this.requestOrder(data) //返回json数据,是否购票成功等等 //把发短信所需要数据都要传入 return Object.assign({}, JSON.parse(res.data), { queryParam: { dateStr, tickAmount, startTime, station } }) }//购票相关逻辑 //调用购票接口 requestOrder(obj){ return axios.post('weixin.xxxx.net/ebus/front/wxQueryController.do?BcTicketBuy', obj, { headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'User-Agent': "Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Mobile/12A365 MicroMessenger/5.4.1 NetType/WIFI", "Cookie": this.cookie } }) } handleInfoUser(){}//通知用户的逻辑 sendMSg(){} //发短信接口 }
到这里,查询余票,购票这两个核心操作已经完成。
目前还剩下,如何通知用户是否购票成功。
之前我尝试过使用qq邮箱的smtp服务,抢票成功后发送邮件通知,但是我觉得吧,并不好用,主要是我没有打开邮箱的习惯,没网也收不到,所以,并没有采纳这个方案。
加上之前我注册过企业认证的公众号,腾讯云免费送了我1000条短信通知,而且短信也比较直观,所以我这里就安装腾讯云的SDK,部署了一套发短信的功能。
2-4腾讯云短信的相关内容
其实看看文档就行了,我也是copy文档,注意看短信单发那部分
cloud.tencent.com/document/pr…
如果跟我一样有企业认证的话,看快速入门这里就行了,一步步跟着操作
看下短信正文, {Number}
这些里面的数字是变量。
就是说短信的模板是固定的,但是里面有 {Number} 的内容可以自定义
调用的时候,里面的数字对应着传过去的参数数组序号,{1}代表数组[0]参数,以此类推
提交审核,审核一般很快就通过,也就是几十万毫秒吧
2-5 开发通知功能
class QueryTicket{ constructor({ data, phoneNumber, cookie, day }) { //constructor代码... } //初始化 async init(){ let ticketList = await this.handleQueryTicket() //如果有余票 if (ticketList.length) { //把余票传入购票逻辑方法,返回短信通知所需要的数据 let resParse = await this.handleBuyTicket(ticketList) //执行通知逻辑 this.handleInfoUser(resParse) } } //查询余票的逻辑 async handleQueryTicket(){ // 查询余票代码... } //调用查询余票接口 requestTicket(){ //调用查询余票接口代码... } //购票相关逻辑 async handleBuyTicket(ticketList){ //购票代码... } //调用购票接口 requestOrder(obj){ //购票接口请求代码... } //通知用户的逻辑 async handleInfoUser(parseData){ //获取上一步购票的response数据和我们拼接的数据 let { returnCode, returnData: { main: { lineName, tradePrice } }, queryParam: { dateStr, tickAmount, startTime, station } } = parseData //如果购票成功,则返回500 if (returnCode === "500") { let res = await this.sendMsg({ dateStr, //日期 tickAmount: tickAmount.slice(0, -1), //总张数 station, //站点 lineName, //巴士名称/路线名称 tradePrice,//总价 startTime,//出发时间 phoneNumber: this.phoneNumber,//手机号 }) //如果发信成功,则不再进行抢票操作 if (res.result === 0 && res.errmsg === "OK") { this.setStop(true) } else { //失败不做任何操作 console.log(res.errmsg) } } else { //失败不做任何操作 console.log(resParse['returnInfo']) } } //发短信接口 sendMSg(){ let { dateStr, tickAmount, station, lineName, phoneNumber, startTime, tradePrice } = obj var appid = 140034324; // SDK AppID 以1400开头 // 短信应用 SDK AppKey var appkey = "asdfdsvajwienin23493nadsnzxc"; // 短信模板 ID,需要在短信控制台中申请 var templateId = 7839; // NOTE: 这里的模板ID`7839`只是示例,真实的模板 ID 需要在短信控制台中申请 // 签名 var smsSign = "测试短信"; // NOTE: 签名参数使用的是`签名内容`,而不是`签名ID`。这里的签名"腾讯云"只是示例,真实的签名需要在短信控制台申请 // 实例化 QcloudSms var qcloudsms = QcloudSms(appid, appkey); var ssender = qcloudsms.SmsSingleSender(); // 这里的params就是短信里面可以自定义的内容,也就是填入{1}{2}..的内容 var params = [dateStr, station, lineName, startTime, tickAmount, tradePrice]; //用promise来封装下异步操作 return new Promise((resolve, reject) => { ssender.sendWithParam(86, phoneNumber, templateId, params, smsSign, "", "", function (err, res, resData) { if (err) { reject(err) } else { resolve(resData) } }); }) } }
如果发信成功,返回 result:0
到这里,大部分需求已经完成了,还剩下一个定时任务
2-6 定时任务
也声明一个类,这里我们用到的是 schedule
// 定时任务 class SetInter { constructor({ timer, fn }) { this.timer = timer // 每几秒执行 this.fn = fn //执行的回调 this.rule = new schedule.RecurrenceRule(); //实例化一个对象 this.rule.second = this.setRule() // 调用原型方法,schedule的语法而已 this.init() } setRule() { let rule = []; let i = 1; while (i < 60) { rule.push(i) i += this.timer } return rule //假设传入的timer为5,则表示定时任务每5秒执行一次 // [1, 6, 11, 16, 21, 26, 31, 36, 41, 46, 51, 56] } init() { schedule.scheduleJob(this.rule, () => { this.fn() // 定时调用传入的回调方法 }); } }
2-7 多个用户抢票
假设我们有两个用户要抢票,所以定义两个obj,实例化下 QueryTicket 类
data: { //用户1 lineId: 111130, vehTime: 0722, startTime: 0751, onStationId: 564492, offStationId: 17990, onStationName: '宝安交通运输局③', offStationName: "深港产学研基地", tradePrice: 0, saleDates: '', beginDate: '', }, phoneNumber: 123123123, cookie: 'JSESSIONID=TESTCOOKIE', day: "17" } var obj2 = { //用户2 data: { lineId: 134423, vehTime: 1820, startTime: 1855, onStationId: 4322, offStationId: 53231, onStationName: '百度国际大厦', offStationName: "裕安路口", tradePrice: 0, saleDates: '', beginDate: '', }, phoneNumber: 175932123124, cookie: 'JSESSIONID=TESTCOOKIE', day: "" } var ticket = new QueryTicket(obj) //用户1 var ticket2 = new QueryTicket(obj2) //用户2 new SetInter({ timer: 1, //每秒执行一次,建议5秒,不然怕被ip拉黑,我这里只是为了方便下面截图 fn: function () { [ticket,ticket2].map(item => { //同时进行两个用户的抢票 if (!item.getStop()) { //调用实例的原型方法,判断是否停止抢票,如果没有则继续抢 item.init() } else { // 如果抢到票了,则不继续抢票 console.log('stop') } }) } })
node index.js 运行下,跑起来了
如果他抢到票的话,我就会收到短信通知:
打开手机,看下订单信息
搞定,收工
写在最后
其实可以在此基础上还能添加更多功能,比如直接抓取登录接口获取cookie,指定路线抢票,还有错误处理啊啥的
因为这个只是我个人使用,所以只有这点功能就足够了。
如果想把它做成一个完整的项目,建议使用ts加持 ,关于ts我推荐阅读这篇JD前端写的文章
juejin.im/post/5d8efe…
希望各位能有所收获
总结
以上所述是小编给大家介绍的Node.js 实现抢票小工具 & 短信通知提醒功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对易盾网络网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!
本文共计4156个文字,预计阅读时间需要17分钟。
在深圳加班是件非常辛苦的事情,尤其是我所在的科技创新园区,人非常多,每天上下班都像春运一样。如果能换到以前的大冲上班那就太幸福了,可惜换不了。
要知道在深圳上班是非常痛苦的事情,特别是我上班的科兴科技园这一块,去的人非常多,每天上班跟春运一样,如果我能换到以前的大冲上班那就幸福了,可惜,换不得。
尤其是我这个站等车的多的一笔,上班公交挤的不行,车满的时候只有少部分人能硬挤上去。通常我只会用两个字来形容这种人:“公交怪”
想当年我朋友瘦的像只猴还能上去,老子身高182体重72kg挤个公交,不成问题,反手一个阻挡,闷声发大财,前面的阿姨你快点阿姨,别磨磨唧唧的,快上去啊阿姨,嗯?你还想挤掉我?你能挤掉我?你能挤掉我!我当场!把车吃了!
....
咳咳,挤公交是不可能挤公交滴,因为今天我发现了一个可以定制路线的网约巴士公众号
但是呢,票经常会被抢光,同时我还我发现,有时候会有人退票,这时候就有空余票了,关键是我不可能时时都在公众号上盯着,于是,我就写了一个抢票+短信通知的小工具
获取接口信息
1.查看页面结构
这个就是订票页面,显示当前月的车票情况,根据图示,红色为已满,绿色为已购,灰色为不可选
如果是可选就是白色的小方块,并且在下面显示余票,如下图所示:
我们打算这么做,
- 定时抓取返回的接口信息
- 根据接口返回值判断是否有余票
好,审查下源代码看下接口信息,等等,微信浏览器没办法审查源代码,于是
2.使用chrome调试微信公众号网页页面
首先面临个问题,如果直接copy公众号网页Url在chrome打开的话,就会显示这个画面,他被302重定向到了这个页面,所以是行不通的,只有获取OAuth2.0授权才能进去
所以我们得先通过抓包工具,知道手机访问微信公众号网页的时候,需要带什么信息过去,这时候我们就得借助抓包工具,因为我电脑是Mac,用不了 Fiddler ,我用的是 Charles 花瓶,就是下面这位仁兄
借助这个工具,我们只需3步就可以轻松搞定手机数据抓包:
- 获取本机IP地址和端口
- 设置代理手机上网
- 依次执行上面两步
2-1 获取本机IP地址和端口
第一步,找到端口号,一般默认是8088,但是为了确认可以打开 Proxy / Proxy Setting 看下,哦原来我之前设置成了8888
然后找到 Charles 的 help / Local IP Address,点击它就会看到自己的本机地址,找到本机地址记下来,然后进行下一步
2-2设置代理手机上网
首先保证手机跟电脑连接的是同一个wifi,然后在wifi设置那里会有设置代理信息,比如我的猴米...不对,小米9手机!设置如下:
输入上一步获取主机名,端口号就ok了
输入完成,点击确定后。 Charles 就会弹出一个对话框,问你是否同意接入代理,点击确定allow就行了。
2-3 用手机访问目标网页
我们用手机访问微信公众号进入到抢票页面后,发现 Charles 已经成功抓包到了网页信息,当我们进入这个抢票页面的时候,他会发起两个请求,一个是获取document文档内容,一个post请求获取票务信息。
仔细分析了下,大概明白了业务逻辑:
整个项目技术站是java+jsp,传统写法,用户身份验证主要是cookie+session方案,前端这一块主要是使用 jQuery 。
当用户进入页面的时候,会携带查询参数,如起始站点,时间,车次等信息和cookie请求document文档, 也就是圈起来的这一块,
而我们想要的核心内容:日历表,一开始是不显示的
因为还要在请求一次
第二次请求,携带cookie和以上的查询参数发起一个post请求,获取当月的车票信息,也就是日历表内容
下面这个是请求当月票务信息,然而发现他返回的是一堆html节点
好吧...估计是获取到之后直接 append 到 div 里面的,然后渲染生成日历表内容
接着在手机上操作,选择两个日期,然后点击下单,发送购票请求,拉取购票接口,我们看下购票接口的请求和返回内容:
看下request 内容,根据字段的意思大概明白是线路,时间,以及车票金额,还有支付方式
在看看返回的内容:返回一个json字符串数据,里面大概涵盖了下单的成功返回码,时间,id号等等信息
2-4 记录所需要的信息内容
根据上面的分析,总结下内容: 整个项目用户身份验证是使用 cookie 和 session 方案,请求数据用的是 form data 方式,请求字段啥的我们也都清楚,唯独有一点,就是请求余票的时候,返回的是html节点代码,而不是我们预期的json数据,这样就有个麻烦,我们没办法一目了然的明白他余票的时候是如何显示的
所以我们只能通过 chrome 进行调试,才能得出他是如何判断余票的。
我们找个记事本,记录下信息,记录的内容有:
- 请求余票接口和购票接口的
url地址 cookie信息 各自的request参数字段user-Agent信息- 各自的
response返回内容
2-5 设置chrome
有以上信息后,我们就可以开始用chrome调试了, 首先打开 More tools / Network conditions
把 user-Agent 填入到 Custom里面
2-6 Charles抓包本地请求
因为我们要把获取到的cookie填入到chrome里面,以我们的用户身份去访问网页,所以我们需要在请求目标地址的时候,改包修改cookie
首先我们需要开启 macOS Proxy ,抓包我们的weixin.xxxx.net/ebus/front/wxQueryController.do?BcTicketCalendar', this.postData, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': "Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Mobile/12A365 MicroMessenger/5.4.1 NetType/WIFI",
"Cookie": this.cookie
}
})
}
handleBuyTicket(){} //购票相关逻辑
requestOrder(){}//调用购票接口
handleInfoUser(){}//通知用户的逻辑
sendMSg(){} //发短信接口
}
来解释下那行正则, cheerio 抓取到的dom是长这样的,第一个 span 内容是日期,第二个是余票数量
所以我们要把它格式化变成这种数组,也就是 ticketList
2-3开发购票功能
首先我们在 init 方法里做个判断,如果有余票才去购票,没有余票购个毛
class QueryTicket{ constructor({ data, phoneNumber, cookie, day }) { //constructor代码... } //初始化 async init(){ let ticketList = await this.handleQueryTicket() //如果有余票 if (ticketList.length) { //把余票传入购票逻辑方法,返回短信通知所需要的数据 let resParse = await this.handleBuyTicket(ticketList) } } //查询余票的逻辑 async handleQueryTicket(){ // 查询余票代码... } //调用查询余票接口 requestTicket(){ //调用查询余票接口代码... } //购票相关逻辑 async handleBuyTicket(ticketList){ let year = new Date().getFullYear() //年份, let month = new Date().getMonth() + 1 //月份,拼接购票日期用得上,因为余票接口只返回几号 let { onStationName,//起始站点名 offStationName,//结束站点名 lineId,//线路id vehTime,//发车时间 startTime,//预计上车时间 onStationId,//上车的站台id offStationId //到站的站台id } = this.data // 初始化的数据 let station = `${onStationName}-${offStationName}` //站点,发短信时候用到:"宝安交通局-深港产学研基地" let dateStr = ""; //车票日期 let tickAmount = "" //总张数 ticketList.forEach(item => { dateStr = dateStr + `${year}-${month}-${item.day},` tickAmount = tickAmount + `${item.ticketLeft}张,` }) var buyTicket = { lineId,//线路id vehTime,//发车时间 startTime,//预计上车时间 onStationId,//上车的站点id offStationId,//目标站点id tradePrice: '5', //金额 saleDates: dateStr.slice(0, -1), payType: '2' //支付方式,微信支付 } // 调用购票接口 let data = querystring.stringify(buyTicket) let res = await this.requestOrder(data) //返回json数据,是否购票成功等等 //把发短信所需要数据都要传入 return Object.assign({}, JSON.parse(res.data), { queryParam: { dateStr, tickAmount, startTime, station } }) }//购票相关逻辑 //调用购票接口 requestOrder(obj){ return axios.post('weixin.xxxx.net/ebus/front/wxQueryController.do?BcTicketBuy', obj, { headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'User-Agent': "Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Mobile/12A365 MicroMessenger/5.4.1 NetType/WIFI", "Cookie": this.cookie } }) } handleInfoUser(){}//通知用户的逻辑 sendMSg(){} //发短信接口 }
到这里,查询余票,购票这两个核心操作已经完成。
目前还剩下,如何通知用户是否购票成功。
之前我尝试过使用qq邮箱的smtp服务,抢票成功后发送邮件通知,但是我觉得吧,并不好用,主要是我没有打开邮箱的习惯,没网也收不到,所以,并没有采纳这个方案。
加上之前我注册过企业认证的公众号,腾讯云免费送了我1000条短信通知,而且短信也比较直观,所以我这里就安装腾讯云的SDK,部署了一套发短信的功能。
2-4腾讯云短信的相关内容
其实看看文档就行了,我也是copy文档,注意看短信单发那部分
cloud.tencent.com/document/pr…
如果跟我一样有企业认证的话,看快速入门这里就行了,一步步跟着操作
看下短信正文, {Number}
这些里面的数字是变量。
就是说短信的模板是固定的,但是里面有 {Number} 的内容可以自定义
调用的时候,里面的数字对应着传过去的参数数组序号,{1}代表数组[0]参数,以此类推
提交审核,审核一般很快就通过,也就是几十万毫秒吧
2-5 开发通知功能
class QueryTicket{ constructor({ data, phoneNumber, cookie, day }) { //constructor代码... } //初始化 async init(){ let ticketList = await this.handleQueryTicket() //如果有余票 if (ticketList.length) { //把余票传入购票逻辑方法,返回短信通知所需要的数据 let resParse = await this.handleBuyTicket(ticketList) //执行通知逻辑 this.handleInfoUser(resParse) } } //查询余票的逻辑 async handleQueryTicket(){ // 查询余票代码... } //调用查询余票接口 requestTicket(){ //调用查询余票接口代码... } //购票相关逻辑 async handleBuyTicket(ticketList){ //购票代码... } //调用购票接口 requestOrder(obj){ //购票接口请求代码... } //通知用户的逻辑 async handleInfoUser(parseData){ //获取上一步购票的response数据和我们拼接的数据 let { returnCode, returnData: { main: { lineName, tradePrice } }, queryParam: { dateStr, tickAmount, startTime, station } } = parseData //如果购票成功,则返回500 if (returnCode === "500") { let res = await this.sendMsg({ dateStr, //日期 tickAmount: tickAmount.slice(0, -1), //总张数 station, //站点 lineName, //巴士名称/路线名称 tradePrice,//总价 startTime,//出发时间 phoneNumber: this.phoneNumber,//手机号 }) //如果发信成功,则不再进行抢票操作 if (res.result === 0 && res.errmsg === "OK") { this.setStop(true) } else { //失败不做任何操作 console.log(res.errmsg) } } else { //失败不做任何操作 console.log(resParse['returnInfo']) } } //发短信接口 sendMSg(){ let { dateStr, tickAmount, station, lineName, phoneNumber, startTime, tradePrice } = obj var appid = 140034324; // SDK AppID 以1400开头 // 短信应用 SDK AppKey var appkey = "asdfdsvajwienin23493nadsnzxc"; // 短信模板 ID,需要在短信控制台中申请 var templateId = 7839; // NOTE: 这里的模板ID`7839`只是示例,真实的模板 ID 需要在短信控制台中申请 // 签名 var smsSign = "测试短信"; // NOTE: 签名参数使用的是`签名内容`,而不是`签名ID`。这里的签名"腾讯云"只是示例,真实的签名需要在短信控制台申请 // 实例化 QcloudSms var qcloudsms = QcloudSms(appid, appkey); var ssender = qcloudsms.SmsSingleSender(); // 这里的params就是短信里面可以自定义的内容,也就是填入{1}{2}..的内容 var params = [dateStr, station, lineName, startTime, tickAmount, tradePrice]; //用promise来封装下异步操作 return new Promise((resolve, reject) => { ssender.sendWithParam(86, phoneNumber, templateId, params, smsSign, "", "", function (err, res, resData) { if (err) { reject(err) } else { resolve(resData) } }); }) } }
如果发信成功,返回 result:0
到这里,大部分需求已经完成了,还剩下一个定时任务
2-6 定时任务
也声明一个类,这里我们用到的是 schedule
// 定时任务 class SetInter { constructor({ timer, fn }) { this.timer = timer // 每几秒执行 this.fn = fn //执行的回调 this.rule = new schedule.RecurrenceRule(); //实例化一个对象 this.rule.second = this.setRule() // 调用原型方法,schedule的语法而已 this.init() } setRule() { let rule = []; let i = 1; while (i < 60) { rule.push(i) i += this.timer } return rule //假设传入的timer为5,则表示定时任务每5秒执行一次 // [1, 6, 11, 16, 21, 26, 31, 36, 41, 46, 51, 56] } init() { schedule.scheduleJob(this.rule, () => { this.fn() // 定时调用传入的回调方法 }); } }
2-7 多个用户抢票
假设我们有两个用户要抢票,所以定义两个obj,实例化下 QueryTicket 类
data: { //用户1 lineId: 111130, vehTime: 0722, startTime: 0751, onStationId: 564492, offStationId: 17990, onStationName: '宝安交通运输局③', offStationName: "深港产学研基地", tradePrice: 0, saleDates: '', beginDate: '', }, phoneNumber: 123123123, cookie: 'JSESSIONID=TESTCOOKIE', day: "17" } var obj2 = { //用户2 data: { lineId: 134423, vehTime: 1820, startTime: 1855, onStationId: 4322, offStationId: 53231, onStationName: '百度国际大厦', offStationName: "裕安路口", tradePrice: 0, saleDates: '', beginDate: '', }, phoneNumber: 175932123124, cookie: 'JSESSIONID=TESTCOOKIE', day: "" } var ticket = new QueryTicket(obj) //用户1 var ticket2 = new QueryTicket(obj2) //用户2 new SetInter({ timer: 1, //每秒执行一次,建议5秒,不然怕被ip拉黑,我这里只是为了方便下面截图 fn: function () { [ticket,ticket2].map(item => { //同时进行两个用户的抢票 if (!item.getStop()) { //调用实例的原型方法,判断是否停止抢票,如果没有则继续抢 item.init() } else { // 如果抢到票了,则不继续抢票 console.log('stop') } }) } })
node index.js 运行下,跑起来了
如果他抢到票的话,我就会收到短信通知:
打开手机,看下订单信息
搞定,收工
写在最后
其实可以在此基础上还能添加更多功能,比如直接抓取登录接口获取cookie,指定路线抢票,还有错误处理啊啥的
因为这个只是我个人使用,所以只有这点功能就足够了。
如果想把它做成一个完整的项目,建议使用ts加持 ,关于ts我推荐阅读这篇JD前端写的文章
juejin.im/post/5d8efe…
希望各位能有所收获
总结
以上所述是小编给大家介绍的Node.js 实现抢票小工具 & 短信通知提醒功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对易盾网络网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

