[实战] 实现抢票小工具&短信通知提醒,附前端面经

接着在手机上操作,选择两个日期,然后点击下单,发送购票请求,拉取购票接口,我们看下购票接口的请求和返回内容:

 看下request 内容,根据字段的意思大概明白是线路,时间,以及车票金额,还有支付方式

在看看返回的内容:返回一个json字符串数据,里面大概涵盖了下单的成功返回码,时间,id号等等信息 

记录所需要的信息内容

根据上面的分析,总结下内容:整个项目用户身份验证是使用 cookie和 session方案,请求数据用的是 form data方式,请求字段啥的我们也都清楚,唯独有一点,就是请求余票的时候,返回的是html节点代码,而不是我们预期的json数据,这样就有个麻烦,我们没办法一目了然的明白他余票的时候是如何显示的

所以我们只能通过 chrome进行调试,才能得出他是如何判断余票的。

我们找个记事本,记录下信息,记录的内容有:

  1. 请求余票接口和购票接口的 url地址

  2. cookie信息

  3. 各自的 request参数字段

  4. user-Agent信息

  5. 各自的 response返回内容

设置chrome

有以上信息后,我们就可以开始用chrome调试了, 首先打开 MoretoolsNetworkconditions

 把 user-Agent填入到 Custom里面

Charles抓包本地请求

因为我们要把获取到的cookie填入到chrome里面,以我们的用户身份去访问网页,所以我们需要在请求目标地址的时候,改包修改cookie

首先我们需要开启 macOSProxy,抓包我们的http请求

 打开chrome访问目标网址,我们可以看到 Charles上已经抓包到了我们访问的目标url地址,然后给目标url地址打上断点,方便调试

 然后再次访问,这时候断点就生效了,弹出一个tab名为 breakpoints,可以看到之所以我们还是不能访问到目标网址,是因为 sessionId不对,所以我们把抓取到的 cookie在填入到里面,点击 execute

 这时候,能够正确跳到目标页面了。

 大概看了下他整体布局,和 jQuery代码 CSS代码,特别是日历表那一块

审查了下元素发现:

  1. 小方块的结构为:

<tdclass=“b”>

这里为日期

如果有余票则显示余票数量

  1. td的样式名为 a代表不可选

  2. 样式名为 e代表已满

  3. 样式名为 d代表已购

  4. 样式名为 b则是我们要找的,代表可选,也就是有余票

到这一步,整个购票流程就清楚了

到时候我们通过Node.js请求的时候,处理返回数据,用正则去判断是否有余票的class名 b ,有余票的话,在获取div里面的余票数量内容就Ok了

Node.js 请求目标接口

==============

分析需要开发的功能点


写代码之前我们需要想好功能点,我们需要什么功能:

  1. 请求余票接口

  2. 定时请求任务

  3. 有余票则自动请求购票接口下订单

  4. 调用腾讯云短信api接口发送短信通知

  5. 多个用户抢票功能

  6. 抢某个日期的票

首先 mkdir ticket 创建名为ticket的文件夹,接着 cd ticket进入文件夹 npm init一路瞎几把回车也无妨。下面开始安装依赖,根据上面的功能需求,我们大概需要:

  1. 请求工具,这里看个人习惯,你也可以使用原生的 http.request,我这里选择用的是 axios,毕竟 axios在node端底层也是调用 http.request

cnpm install axios --save

  1. 定时任务 node-schedule

cnpm install node-schedule --save

  1. node端选择dom节点工具 cheerio

cnpm install cheerio --save

  1. 腾讯发短信的依赖包 qcloudsms_js

cnpm install qcloudsms_js

  1. 热更新包,诺豆的妈妈, nodemon (其实不用也可以)

cnpm install nodemon --save-dev

开发请求余票接口


接着 touch index.js创建核心js文件,开始编码:

首先引入所有依赖

const axios = require(‘axios’)

const querystring = require(“querystring”); //序列化对象,用qs也行,都一样

let QcloudSms= require(“qcloudsms_js”);

let cheerio = require(‘cheerio’);

let schedule = require(‘node-schedule’);

然后我们先定义请求参数,来一个obj

let obj = {

data: {

lineId: 111130, //路线id

vehTime: 0722, //发车时间,

startTime: 0751, //预计上车时间

onStationId: 564492, //预定的站点id

offStationId: 17990,//到站id

onStationName: ‘宝安交通运输局③’, //预定的站点名称

offStationName: “深港产学研基地”,//预定到站名称

tradePrice: 0,//总金额

saleDates: ‘17’,//车票日期

beginDate: ‘’,//订票时间,滞空,用于抓取到余票后填入数据

},

phoneNumber: 123123123, //用户手机号,接收短信的手机号

cookie: ‘JSESSIONID=TESTCOOKIE’, // 抓取到的cookie

day: “17”//定17号的票,这个主要是用于抢指定日期的票,滞空则为抢当月所有余票

}

接着声明一个名为 queryTicket的类,为啥要用类呢,因为基于第五个需求点,多个用户抢票的时候,我们分别 new一下就行了,

同时我们希望能够记录请求余票的次数,和当抢到票后自动停止查询余票的操作,所以给他加上个计数变量 times和是否停止的变量,布尔值 stop

编写代码:

class QueryTicket{

/**

*Creates an instance of QueryTicket.

  • @param {Object} { data, phoneNumber, cookie, day }

  • @param data {Object} 请求余票接口的requery参数

  • @param phoneNumber {Number} 用户手机号,短信需要用到

  • @param cookie {String} cookie信息

  • @params day {String} 某日的票,如’18’

  • @memberof QueryTicket 请求余票接口

*/

constructor({ data, phoneNumber, cookie, day }) {

this.data = data

this.cookie = cookie

this.day = day

this.phoneNumber = phoneNumber

this.postData = querystring.stringify(data)

this.times = 0; //记录次数

let stop = false //通过特定接口才能修改stop值,防止外部随意串改

this.getStop = function () { //获取是否停止

return stop

}

this.setStop = function (ifStop) { //设置是否停止

stop = ifStop

}

}

}

下面开始定义原型方法,为了方便维护,我们把逻辑拆分成各个函数

class QueryTicket{

constructor({ data, phoneNumber, cookie, day }) {

//constructor代码…

}

init(){}//初始化

handleQueryTicket(){}//查询余票的逻辑

requestTicket(){} //调用查询余票接口

handleBuyTicket(){} //购票相关逻辑

requestOrder(){}//调用购票接口

handleInfoUser(){}//通知用户的逻辑

sendMSg(){} //发短信接口

}

所有数据都是基于查询余票的操作,因此我们先开发这部分功能

class QueryTicket{

constructor({ data, phoneNumber, cookie, day }) {

//constructor代码…

}

//初始化,因为涉及到异步请求,所以我们使用async await

async init(){

let ticketList = await this.handleQueryTicket() //返回查询到的余票数组

}

//查询余票的逻辑

handleQueryTicket(){

let ticketList = [] //余票数组

let res = await this.requestTicket()

this.times++ //计数器,记录请求查询多少次

let str = res.data.replace(/\/g, “”) //格式化返回值

let $ = cheerio.load(<div class="main">${str}</div>) // cheerio载入查询接口response的html节点数据

let list = $(“.main”).find(“.b”) //查找是否有余票的dom节点

// 如果没有余票,打印出请求多少次,然后返回,不执行下面的代码

if (!list.length) {

console.log(用户${this.phoneNumber}:无票,已进行${this.times}次)

return

}

// 如果有余票

list.each((idx, item) => {

let str = KaTeX parse error: Expected 'EOF', got '&' at position 46: …21</span><span>&̲x4F59;0

//最后一个span 的内容其实"余0",也就是无票,只不过是被转码了而已

//因此要在下一步对其进行格式化

let arr = str.split(/|</span>|&#x4F59;/).filter(item => !!item === true)

let data = {

day: arr[0],

ticketLeft: arr[1]

}

//如果是要抢指定日期的票

if (this.day) {

//如果有指定日期的余票

if (parseInt(data.day) === parseInt(data.day)) {

ticketList.push(data)

}

} else {

//如果不是,则返回查询到的所有余票

ticketList.push(data)

}

})

return ticketList

}

//调用查询余票接口

requestTicket(){

return axios.post(‘http://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 

开发购票功能


首先我们在 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}张,

})

let 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(‘http://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”,

小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注:前端)
img

t-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”,

小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-KSDvJYXK-1710899759064)]
[外链图片转存中…(img-tV7kkYzc-1710899759065)]
[外链图片转存中…(img-s2c26Qwr-1710899759066)]
[外链图片转存中…(img-G4MWx3HQ-1710899759066)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注:前端)
[外链图片转存中…(img-GOWGoiSi-1710899759066)]

  • 28
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值