Node.js реализует виджет захвата билетов и напоминание о SMS-уведомлениях

Node.js
Node.js реализует виджет захвата билетов и напоминание о SMS-уведомлениях

написано в предисловии

Вы должны знать, что работать в Шэньчжэне очень больно, особенно в научно-технологическом парке Кэсин, где я работаю. Там много людей, которые ходят на работу. Каждый день на работу похож на Праздник Весны. К сожалению, это невозможно. не подлежит замене.

В частности, я очень долго жду автобуса на этой станции.Автобус на работу не переполнен.Когда автобус полный, в него могут протиснуться лишь несколько человек. Обычно я использую только два слова, чтобы описать такого человека: «автобусный монстр».

Я думаю, что мой друг был худым, как обезьяна, и все еще мог подняться. Я был 182 и весил 72 кг, чтобы протиснуться в автобусе. Это не было проблемой. Я заблокировал его ударом слева и сделал состояние. ? Ты все еще хочешь меня выдавить? Ты можешь выдавить меня? Ты можешь выдавить меня! Я на месте! Ешьте машину!

....

Кхм, втиснуть автобус невозможно, потому что сегодня я нашел общедоступный номер для онлайн-бронирования автобусов, который может настроить маршрут [xxxxxx]

Однако билеты часто грабят.В то же время я также обнаружил, что иногда люди возвращают билет, и тогда будут бесплатные билеты.Ключ в том, что я не могу постоянно следить за пабликом. , так я и написал Гаджет для захвата билетов + смс оповещение

Получить информацию об интерфейсе

Посмотреть структуру страницы

Это страница бронирования билетов, показывающая статус билетов на текущий месяц, на картинке красный заполнен, зеленый куплен, а серый недоступен.

Если это необязательно, это маленький белый квадрат, а оставшиеся голоса отображаются ниже, как показано на следующем рисунке:

Мы собираемся сделать это,

  1. Периодически собирать возвращенную информацию об интерфейсе
  2. Определить, есть ли оставшиеся голоса в соответствии с возвращаемым значением интерфейса

ОК, просмотрите исходный код, чтобы увидеть информацию об интерфейсе и т. д. Браузер WeChat не может просмотреть исходный код, поэтому

Используйте хром для отладки официальной веб-страницы учетной записи WeChat

Прежде всего есть проблема.Если напрямую скопировать Url страницы официального аккаунта и открыть его в хроме, то будет отображаться такой экран.Он перенаправляется на эту страницу по 302, поэтому работать не будет.Только после получения OAuth2. 0 авторизация можете войти.

Поэтому мы должны сначала использовать инструмент захвата пакетов, чтобы узнать, какую информацию нам нужно предоставить, когда мы посещаем официальную веб-страницу учетной записи WeChat на мобильном телефоне.В настоящее время мы должны использовать инструмент захвата пакетов, потому что мой компьютер Mac и я не могу его использовать.Fiddler, Я используюCharlesВаза - человек внизу

С помощью этого инструмента мы можем легко захватить данные мобильных телефонов всего за 3 шага:

  1. Получить локальный IP-адрес и порт
  2. Настроить мобильный доступ в интернет через прокси
  3. Выполните два вышеуказанных шага по очереди

Получить локальный IP-адрес и порт

Первый шаг — найти номер порта, который обычно равен 8088 по умолчанию, но его можно открыть для подтверждения.Proxy/Proxy SettingСмотри, о, так я установил его на 8888 раньше

Затем найдитеCharlesизhelp/Local IP Address, щелкните его, вы увидите свой собственный локальный адрес, найдите локальный адрес и запишите его, а затем перейдите к следующему шагу.

Настроить мобильный доступ в интернет через прокси

Сначала убедитесь, что мобильный телефон и компьютер подключены к одному и тому же Wi-Fi, а затем в настройках Wi-Fi появится информация о прокси, например, мой обезьяний рис ... Нет, мобильный телефон Xiaomi 9! Настройки следующие:

Введите предыдущий шаг, чтобы получить имя хоста, номер порта в порядке.

После завершения ввода нажмите ОК.CharlesПоявится диалоговое окно с вопросом, согласны ли вы на доступ к прокси-серверу, нажмите «ОК», чтобы разрешить.

Посетите целевую страницу на своем телефоне

После того, как мы получили доступ к общедоступной учетной записи WeChat [xxxx] с нашего мобильного телефона и вошли на страницу получения билетов, мы обнаружили, чтоCharlesПакет был успешно перехвачен на информацию о веб-странице.Когда мы заходим на страницу захвата билетов, он инициирует два запроса: один для получения содержимого документа, а другой — почтовый запрос для получения информации о билете.

После тщательного анализа я, наверное, понял бизнес-логику:

Вся технологическая станция проекта представляет собой java + jsp, традиционное письмо, аутентификация пользователя в основном представляет собой схему cookie + сеанс, в основном используется внешний интерфейс.jQuery.

Когда пользователь входит на страницу, он будет содержать параметры запроса, такие как начальный сайт, время, номер поезда и другую информацию, а также документ документа запроса файлов cookie, Это обведенная часть.

И основной контент, который нам нужен: таблица календаря, которая сначала не отображается.

потому что он снова спрашивает

Второй запрос — инициировать почтовый запрос с файлом cookie и указанными выше параметрами запроса для получения информации о билете текущего месяца, то есть содержимого таблицы календаря.

Далее идет запрос информации о тикете текущего месяца, но обнаруживается, что он возвращает кучу html-узлов

Ну... считается, что сразу после полученияappendприбытьdivвнутри, а затем отображать содержимое таблицы календаря

Затем работайте на мобильном телефоне, выберите две даты, затем нажмите заказ, отправьте запрос на покупку билета и потяните интерфейс покупки билета.Давайте посмотрим на запрос и вернемся к содержимому интерфейса покупки билета:

Посмотрите на содержание запроса, по смыслу полей вы наверняка сможете понять очередь, время и сумму билета, а также способ оплаты.

Взгляните на возвращенный контент: верните данные строки json, которые, вероятно, охватывают код успешного возврата, время, идентификационный номер и другую информацию о заказе.

Запишите необходимый информационный контент

На основании вышеприведенного анализа можно сделать следующие выводы: Вся аутентификация пользователя проекта используетcookieиsessionсхема, данные запросаform dataСпособ, поля запроса мы тоже знаем, единственное, что при запросе оставшихся голосов вместо ожидаемых нами данных json возвращается html код узла, поэтому тут беда, с первого взгляда понять не можем Как он показывает, когда у него есть оставшиеся голоса?

Так что мы можем только пройтиchromeОтладка, чтобы узнать, как он оценил оставшиеся голоса.

Давайте найдем блокнот и запишем информацию.Записанное содержимое:

  1. Запросить оставшийся интерфейс билета и интерфейс покупки билетаurlадрес
  2. cookieИнформация
  3. Соответствующийrequestполе параметра
  4. user-AgentИнформация
  5. Соответствующийresponseвернуть содержимое

установить хром

С приведенной выше информацией мы можем начать отладку с хромом, первый открытыйMore tools/Network conditions

Пучокuser-AgentзаполнятьCustomв

Чарльз фиксирует локальные запросы

Поскольку мы хотим заполнить полученный файл cookie в Chrome и получить доступ к веб-странице как наш пользователь, нам нужно изменить пакет и изменить файл cookie при запросе целевого адреса.

Сначала нам нужно включитьmacOS Proxy, чтобы захватить наш http-запрос

Откройте целевой URL-адрес Chrome Access, мы можем увидетьCharlesЯ уже захватил целевой URL-адрес, который мы посетили, а затем поставил точку останова на целевой URL-адрес, что удобно для отладки.

Затем зайдите снова, точка останова вступит в силу в это время, и вкладка с именемbreak points, вы можете видеть, что причина, по которой мы все еще не можем получить доступ к целевому URL, заключается в том, чтоsessionIdНет, так что мы хватаемcookieПосле заполнения нажмитеexecute

В это время вы можете правильно перейти на целевую страницу.

Вероятно, посмотрел на его общую компоновку, иjQueryкодCSSкод, особенно часть календаря

Были осмотрены и обнаружены следующие элементы:

  1. Структура малого квадрата такова:
<td class="b">
<span>这里为日期</span>
<span>如果有余票则显示余票数量</span>
</td>
  1. имя стиля tdaПредставитель не является обязательным
  2. стиль по имениeДелегаты полны
  3. стиль по имениdОт имени купленного
  4. стиль по имениbИменно то, что мы ищем, а значит необязательно, то есть больше голосов

На данный момент весь процесс покупки билетов понятен.

В то время, когда мы запрашиваем через Node.js, мы будем обрабатывать возвращенные данные и использовать регулярные выражения, чтобы определить, есть ли оставшиеся голоса.b, если есть оставшиеся голоса, можно получить количество оставшихся голосов в div

Целевой интерфейс запроса Node.js

Проанализируйте функциональные точки, которые необходимо развивать

Прежде чем писать код, нам нужно подумать о функциональных точках и о том, какие функции нам нужны:

  1. Интерфейс запроса оставшегося билета
  2. Запланированные задачи запроса
  3. Если остались билеты, он автоматически запросит интерфейс покупки билетов для размещения заказа.
  4. Позвоните в Tencent Cloud SMS API, чтобы отправить SMS-уведомление
  5. Функция захвата нескольких пользовательских билетов
  6. взять билет на определенную дату

во-первыхmkdir ticketСоздайте папку с именем ticket, затемcd ticketвойти в папкуnpm initМожно вернуться несколько раз по пути. Приступим к установке зависимостей.Согласно вышеуказанным функциональным требованиям нам, вероятно, потребуется:

  1. Инструмент запроса, здесь зависит от личных привычек, также можно использовать нативныйhttp.request, я выбираю использовать здесьaxios,после всегоaxiosЕго также называют внизу стороны узлаhttp.request
cnpm install axios --save
  1. задача на времяnode-schedule
cnpm install node-schedule --save
  1. Инструмент выбора узла dom на стороне узлаcheerio
cnpm install cheerio --save
  1. Пакет зависимостей для текстовых сообщений Tencentqcloudsms_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Просто сделай это прямо сейчас,

В то же время мы надеемся, что сможем записывать количество запросов на оставшиеся билеты, и автоматически останавливать операцию запроса оставшихся билетов после захвата билетов, поэтому добавим к нему переменную count.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 = $(item).html() //str这时格式是<span>21</span><span>&$x4F59;0</span>
      //最后一个span 的内容其实"余0",也就是无票,只不过是被转码了而已
      //因此要在下一步对其进行格式化
      let arr = str.split(/<span>|<\/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Захваченный дом выглядит так, первый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",
        "Cookie": this.cookie
      }
    })
    }
    handleInfoUser(){}//通知用户的逻辑
    sendMSg(){} //发短信接口
}

На данный момент две основные операции запроса оставшихся билетов и покупки билетов завершены.

Осталось еще одно, как уведомить пользователя об успешной покупке билета.

Раньше я пытался использовать службу smtp почтового ящика qq и отправил уведомление по электронной почте после того, как билет был успешно получен, но я думаю, что это непросто, в основном потому, что у меня нет привычки открывать свой почтовый ящик, и я не могу получить его без интернета, поэтому я не принял эту программу.

Кроме того, я ранее зарегистрировал официальную учетную запись сертификации предприятия, Tencent Cloud отправил мне 1000 SMS-уведомлений бесплатно, а текстовые сообщения относительно интуитивно понятны, поэтому я установил здесь SDK Tencent Cloud и развернул набор функций для отправки текстовых сообщений.

Контент, связанный с Tencent Cloud SMS

На самом деле просто посмотрите документ, я тоже копирую документ, обратите внимание на часть текстового сообщения

cloud.Tencent.com/document/cheat…

Если у вас есть корпоративная сертификация, как у меня, просто ознакомьтесь с кратким руководством здесь и следуйте инструкциям шаг за шагом.

Посмотрите на текст сообщения,{Number}Числа внутри них являются переменными.

То есть шаблон смс фиксированный, но есть{Number}содержание может быть настроено

При вызове номер внутри соответствует порядковому номеру переданного массива параметров, {1} представляет параметр массива[0] и т. д.

Отправьте на проверку, а проверка вообще проходит быстро, то есть сотни тысяч миллисекунд.

Разработать функцию уведомления

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
    let appid = 140034324;  // SDK AppID 以1400开头
    // 短信应用 SDK AppKey
    let appkey = "asdfdsvajwienin23493nadsnzxc";
    // 短信模板 ID,需要在短信控制台中申请
    let templateId = 7839;  // NOTE: 这里的模板ID`7839`只是示例,真实的模板 ID 需要在短信控制台中申请
    // 签名
    let smsSign = "测试短信";  // NOTE: 签名参数使用的是`签名内容`,而不是`签名ID`。这里的签名"腾讯云"只是示例,真实的签名需要在短信控制台申请
    // 实例化 QcloudSms
    let qcloudsms = QcloudSms(appid, appkey);
    let ssender = qcloudsms.SmsSingleSender();
    // 这里的params就是短信里面可以自定义的内容,也就是填入{1}{2}..的内容
    let 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

На данный момент большая часть требований выполнена, и осталось еще одно запланированное задание.

задача на время

Также объявите класс, здесь мы используем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() // 定时调用传入的回调方法
    });
  }
}

Несколько пользователей захватывают билеты

Предположим, у нас есть два пользователя, которые хотят получить билеты, поэтому определите два объекта и создайте их экземпляры.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"
}
let 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: "" 
}
let ticket = new QueryTicket(obj) //用户1
let 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, указание маршрутов для получения билетов и обработка ошибок.

Стоит отметить, что интерфейс запроса не может быть слишком частым, лучше всего контролировать частоту раз в 5 секунд, иначе это доставит неприятности другим, и его легко заблокировать по ip.

Если вы хотите сделать его законченным проектом, рекомендуется использовать благословение ts , насчет тс, рекомендую прочитать эту статью, написанную фронтендом JD

nuggets.capable/post/684490…

Надеюсь, вы все получите что-то

специальное заявление

Эта статья предназначена только для технического обмена, код в статье предназначен только для учебных целей, если есть какие-либо нарушения, свяжитесь со мной, чтобы удалить

Нерегулярно обновляйте различные интересные варианты использования js, обратите внимание, не рекламируйте, не полагайтесь на эту просто еду.