怎样用node+puppeteer写个抢题插件

1,449 阅读2分钟

为啥要写个抢题插件

之前我姐需要在一个网站上抢题,题是不定时随机放出来的,让我写个js脚本帮她抢题。抢到以后发现很多人需要代抢,于是我就用puppeteer写了个插件,小赚了2万块吧QAQ,希望大家遇到同样需求的时候也能用上。

开撸

首先我需要管理一个需要抢题的账号密码列表,用来登录网站。要编辑够方便,我第一时间想到的就是node-xlsx,需要批量登录用的是puppeteer

const fs = require('fs')
const puppeteer = require('puppeteer')
const xlsx = require('node-xlsx')
const buy = xlsx.parse('./excel/buy2.xlsx')

接下来就需要遍历excel数据,读取里面的账号密码,用来登录网站,

async function shili(item, index) {
  const browser = await puppeteer.launch({
    headless,
    defaultViewport: {
      width: 1400,
      height: 800,
    }
  })
  const page = await browser.newPage()
  // 响应拦截
  const blockTypes = new Set(['image', 'media', 'font']);
  await page.setRequestInterception(true); //开启请求拦截
  page.on('request', request => {
    const type = request.resourceType();
    const shouldBlock = blockTypes.has(type);
    if (shouldBlock) {
      //直接阻止请求
      return request.abort();
    } else {
      //对请求重写
      return request.continue(
        //可以对 url,method,postData,headers 进行覆盖
      );
    }
  });
  try {
    await page.goto('http://www.jxtyevduyung.com')
  } catch {
    console.log('网络出问题了 ' + index)
    await browser.close()
    return
  }

  let tab2 = await page.$('.login-role:last-child')
  await tab2.click()
  const name = await page.$('#username')
  const psd = await page.$('#password')
  await name.type(item[1] + '')
  await psd.type(item[2] + '')
  let loginbtn = await page.$('#sso_login')
  loginbtn.click()
}  
var task = function () {
  let start = 0
  let interval = setInterval(() => {
    shili(buyData[start], start)
    start += 1;
    if (start == buyData.length) {
      clearInterval(interval)
    }
  }, 5000);
}

每一个账号都会实例一个浏览器出来,这个比较耗电脑性能,同时打开几十个浏览器的话会卡死。所以我用了定时器5秒启动一个浏览器,接下来就是用puppeteer的page.$()方法获取账号密码输入框,用type()方法输入账号密码,然后click()方法模拟点击登录。再接下来就是轮询查找题目了

await page.evaluate((buyItem) => {
    ajax() 轮询查找题目找到匹配的再发ajax()请求抢题
},buyItem)

puppeteer的evaluate()方法相当于往浏览器控制台里注入一段代码并运行,方法传参只能通过evaluate第二个参数声明后才有效,需要多个参数的话就只能组合成一个参数再传比如对象。

最后就是抢到题怎么记录的问题了,通过evaluate注入方法的话我在外部是无法获取ajax请求结果的(return 无效亲测) 所以我直接用node启了http服务,抢题成功我直接访问这个地址不就能记录了吗?

const http = require('http')
let list = []
let timelist = []
http.createServer(function(req,res){
  let exp = /[?]/
  if(exp.test(req.url)){
    list.push(req.url.split('?account=')[1])
    timelist.push(new Date().toString())
  }
  let send = list.map((item,index)=>`<p style="color:red;fontSize:16px">${item} <span style="color:black;float:right">${timelist[index]}</span> </p>`).join('')
  let html = `<div style="padding:80px;width:600px;"><h>抢题结果</h>${send}</div>`
  res.setHeader('Content-Type', 'text/html; charset=utf-8');
  res.end(html)
}).listen(3000)
// 抢题成功直接访问localhost:3000
location.href = `http://localhost:3000/?account=${buyItem.account}`

再整个.bat文件作为桌面启动快捷方式,巴适

cd /
cd d:
cd personnal
npm run request

最后,第一次写文章,比较水。请大家多包涵。 后面会写一篇用puppeteer实现一个项目预渲染和骨架屏共存的文章,通过简单配置就能实现一个项目某些页面是预渲染个性化页面骨架屏,甚至可以部署到服务端,通过node-schedule实现定时更新,干货满满。给个赞再走吧