• Node.js 实现抢票小工具&短信通知提醒


    写在前言

    要知道在深圳上班是非常痛苦的事情,特别是我上班的科兴科技园这一块,去的人非常多,每天上班跟春运一样,如果我能换到以前的大冲上班那就幸福了,可惜,换不得。

    尤其是我这个站等车的多的一笔,上班公交挤的不行,车满的时候只有少部分人能硬挤上去。通常我只会用两个字来形容这种人:“公交怪”

    想当年我朋友瘦的像只猴还能上去,老子身高182体重72kg挤个公交,不成问题,反手一个阻挡,闷声发大财,前面的阿姨你快点阿姨,别磨磨唧唧的,快上去啊阿姨,嗯?你还想挤掉我?你能挤掉我?你能挤掉我!我当场!把车吃了!

    咳咳,挤公交是不可能挤公交滴,因为今天我发现了一个可以定制路线的网约巴士公众号【xxxxxx】

    但是呢,票经常会被抢光,同时我还我发现,有时候会有人退票,这时候就有空余票了,关键是我不可能时时都在公众号上盯着,于是,我就写了一个抢票+短信通知的小工具

    获取接口信息

    查看页面结构

    这个就是订票页面,显示当前月的车票情况,根据图示,红色为已满,绿色为已购,灰色为不可选

    如果是可选就是白色的小方块,并且在下面显示余票,如下图所示:

    我们打算这么做,

    1. 定时抓取返回的接口信息
    2. 根据接口返回值判断是否有余票

    好,审查下源代码看下接口信息,等等,微信浏览器没办法审查源代码,于是

    使用chrome 调试微信公众号网页页面

    首先面临个问题,如果直接copy公众号网页Url在chrome打开的话,就会显示这个画面,他被302重定向到了这个页面,所以是行不通的,只有获取OAuth2.0授权才能进去

    所以我们得先通过抓包工具,知道手机访问微信公众号网页的时候,需要带什么信息过去,这时候我们就得借助抓包工具,因为我电脑是Mac,用不了Fiddler,我用的是Charles花瓶,就是下面这位仁兄

    借助这个工具,我们只需3步就可以轻松搞定手机数据抓包:

    1. 获取本机IP地址和端口
    2. 设置代理手机上网
    3. 依次执行上面两步

    获取本机IP地址和端口

    第一步,找到端口号,一般默认是8088,但是为了确认可以打开Proxy/Proxy Setting看下,哦原来我之前设置成了8888

    然后找到Charleshelp/Local IP Address,点击它就会看到自己的本机地址,找到本机地址记下来,然后进行下一步

    设置代理手机上网

    首先保证手机跟电脑连接的是同一个wifi,然后在wifi设置那里会有设置代理信息,比如我的猴米…不对,小米9手机!设置如下:

    输入上一步获取主机名,端口号就ok了

    输入完成,点击确定后。Charles就会弹出一个对话框,问你是否同意接入代理,点击确定allow就行了。

    用手机访问目标网页

    我们用手机访问微信公众号【xxxx】进入到抢票页面后,发现Charles已经成功抓包到了网页信息,当我们进入这个抢票页面的时候,他会发起两个请求,一个是获取document文档内容,一个post请求获取票务信息。

    仔细分析了下,大概明白了业务逻辑:

    整个项目技术站是java+jsp,传统写法,用户身份验证主要是cookie+session方案,前端这一块主要是使用jQuery

    当用户进入页面的时候,会携带查询参数,如起始站点,时间,车次等信息和cookie请求document文档, 也就是圈起来的这一块,

    而我们想要的核心内容:日历表,一开始是不显示的

    因为还要在请求一次

    第二次请求,携带cookie和以上的查询参数发起一个post请求,获取当月的车票信息,也就是日历表内容

    下面这个是请求当月票务信息,然而发现他返回的是一堆html节点

    好吧…估计是获取到之后直接appenddiv里面的,然后渲染生成日历表内容

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

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

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

    记录所需要的信息内容

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

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

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

    1. 请求余票接口和购票接口的url地址
    2. cookie信息
    3. 各自的request参数字段
    4. user-Agent信息
    5. 各自的response返回内容

    设置chrome

    有以上信息后,我们就可以开始用chrome调试了, 首先打开More tools/Network conditions

    user-Agent填入到Custom里面

    Charles抓包本地请求

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

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

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

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

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

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

    审查了下元素发现:

    1. 小方块的结构为:
    <td class="b">
    <span>这里为日期span>
    <span>如果有余票则显示余票数量span>
    td>
    
    • 1
    • 2
    • 3
    • 4
    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
    1. 定时任务 node-schedule
    cnpm install node-schedule --save
    
    • 1
    1. node端选择dom节点工具 cheerio
    cnpm install cheerio --save
    
    • 1
    1. 腾讯发短信的依赖包 qcloudsms_js
    cnpm install qcloudsms_js 
    
    • 1
    1. 热更新包,诺豆的妈妈,nodemon (其实不用也可以)
    cnpm install nodemon --save-dev
    
    • 1

    开发请求余票接口

    接着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');
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    然后我们先定义请求参数,来一个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号的票,这个主要是用于抢指定日期的票,滞空则为抢当月所有余票
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

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

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

    编写代码:

    • 相关阅读:
      rsync 远程同步
      论文浅尝 | 探索用于归纳型知识图谱补全的关系语义
      MySQL数据库:2、MySQL的下载与安装、基本使用、系统服务制作
      Kotlin 开发Android app(六):Kotlin 中的空判断 问号和感叹号
      人工智能:支持向量机SVM 练习题(带解析)
      ChatGPT API - 笔记
      计算机网络-数据链路层
      redis运维(八)数据类型(一)字符串
      购物车下单实现思路【vue】
      受众分析与卸载分析全面升级,HMS Core分析服务6.6.0版本上新
    • 原文地址:https://blog.csdn.net/weixin_45506717/article/details/133745989