jsDom使用以及简易滑动验证码跳过

黄粱一梦2024-03-0816

jsDom介绍

sdom 是一个纯粹由 JavaScript 实现的一系列 Web 标准,特别是 WHATWG 组织制定的 DOM 和 HTML 标准,用于在 Node.js 中使用。大体上来说,该项目的目标是模拟足够的 Web 浏览器子集,以便用于测试和挖掘真实世界的 Web 应用程序。

jsdom中文文档地址

安装使用jsDom

npm i jsdom

目录结构

Alt text

app.js

const express = require('express')

const  app = express()
const router = require('./router/index')
const path = require('path')

app.use(router)

app.use('/public',express.static(path.join(__dirname,'./template')))


app.listen(20315,() => {
    console.log('app run on server http://localhost:20315')
})

router/index.js

const express = require('express')
const moment = require('moment')
const jsdom = require('jsdom')
const { JSDOM } = jsdom;
const axios = require('axios')
const router = express.Router()
const uuid = require('uuid')
function allFn(req,res,next){
    res.set('Access-Control-Allow-Origin','*')
    console.log("Method::",req.method+"---","Path::",req.path+"---","createTime::",moment().format('YYYY-MM-DD HH:mm:ss'))
    next()
}

router.use(allFn)

router.get('/test',(req, res) => {
    let responseStr = '本次请求id:::'+uuid.v1()
    console.log(responseStr)
    res.send(responseStr)
})

const storage = {
    data: {},
    getItem(key) {
        return this.data[key];
    },
    setItem(key, value) {
        this.data[key] = value;
    },
    removeItem(key) {
        delete this.data[key];
    },
    clear() {
        this.data = {};
    }
};

    getHtml('http://localhost:20315/public/index.html').then(val => {
        let template = val

        let jsDomInstance = new JSDOM(template,{
            runScripts: "dangerously",
            resources: "usable",
        })
        let {window} = jsDomInstance
        window.localStorage = storage;
        let stemp = window.document.createElement('script')
        stemp.text = `
        window.onload = function(){
        const done = false // 是否开启定时脚本
        console.log('自定义脚本插入成功·····')
        if(done) {
            setInterval(() => {
                console.log('插入脚本定时请求---200毫秒一次')
                let btn = document.querySelector('button')
                btn.click()
            },2000)
        }else {
                let btn = document.querySelector('button')
                btn.click()
        }
        }
        `
        window.document.body.appendChild(stemp)
        /* 插入axios脚本 */
        let axiosScript = window.document.createElement('script')
        axiosScript.src = 'https://unpkg.com/axios/dist/axios.min.js'
        window.document.body.appendChild(axiosScript)

        /* 插入js拖动校验脚本 */
        let moveScript = window.document.createElement('script')
        moveScript.text = `
                        /*创建event*/
                         let eve = sideInner.dispatchEvent(new MouseEvent('mousedown'),{
                             bubbles: false, // 默认值false, 事件是否冒泡
                             cancelable: false,  // 默认值false, 事件能否被取消
                             composed: false  // 默认值false, 事件是否会在影子DOM根节点之外触发侦听器。
                         })
                    
                    
                          // sideInner.dispatchEvent(new MouseEvent('mousemove'),{
                          //     clientX:320,
                          //     clientY:181
                          // })
                    
                    
                          // 创建鼠标移动事件
                          let executionCount = 452 //执行次数
                          console.log('开始分发事件')
                          for(let i=100;i<=executionCount;i++) {
                              setTimeout(()=> {
                                  sideInner.dispatchEvent(new MouseEvent('mousemove',{
                                      bubbles: true,
                                      cancelable: true,
                                      view: window,
                                      clientX: i,
                                      clientY: 181,
                                  }))
                                  if(i == executionCount) {
                                      window.dispatchEvent(new MouseEvent('mouseup'),{})
                                      console.log('解除自动分发事件')
                                  }
                              },i*3)
                          }
                        `
        window.document.body.appendChild(moveScript)
    })




let targetPath = 'https://whriten.gitee.io'

function getHtml(targetPath){
    return  new Promise((resolve, reject) => {
        axios.get(targetPath).then(response => {
            resolve(response.data)
        }).catch(err => {
            reject(err)
        })
    })
}

getHtml(targetPath).then(res => {
    let jsDomInstance = new JSDOM(res,{
        runScripts: "dangerously",
        resources: "usable"
    })
    let {window} = jsDomInstance.window
    let script = window.document.createElement('script')
    let host = 'https://whriten.gitee.io'
    let axiosScript = window.document.createElement('script')
    axiosScript.src = 'https://unpkg.com/axios/dist/axios.min.js'
    window.document.body.appendChild(axiosScript)
    let pathUrl = ''
    script.text = `
           window.onload = function(){
           let host = 'https://whriten.gitee.io'
            console.log('插入脚本被加载····')
           }
    `
    pathUrl = 'https://whriten.gitee.io/blogs/other/guide.html'
    // axios(pathUrl).then(val => {
    //     console.log('val',val.data)
    // })
    window.document.body.appendChild(script)
})



module.exports = router

插入执行js脚本

jsdom最强大的能力就是可以执行jsdom内部的脚本。这些脚本可以修改页面内容并访问 jsdom 实现的所有 Web 平台 API。

::: danger
jsDom默认关闭引入脚本的执行,需要我们在配置项中开启 runScripts: "dangerously",,resources: "usable",
:::

示例>

目标效果

当拖动到最右边时候完成验证发起请求

Alt text

验证成功之后发送请求

Alt text

接收请求

Alt text

Alt text

代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        *{padding:0px;margin:0px;}
        .box-out {
            margin-left: 100px;
            margin-top: 100px;
            position: relative;
            width: 400px;
            height: 30px;
            background: #ececec;
        }
        .box-inner {
            position: absolute;
            z-index: 2;
            left: 0px;
            top: 50%;
            transform: translateY(-50%);
            width: 50px;
            height: 30px;
            background-color: #fff;
            box-shadow: 0px 0px 10px #ccc;
            cursor: pointer;
        }
        .in {
            position: absolute;
            left: 0px;
            top: 50%;
            transform: translateY(-50%);
            background-color: greenyellow;
            width: 30px;
            height: 100%;
        }
        .box-inner::after {
            position: absolute;
            content: "";
            top: 50%;
            transform: translate(-50%,-50%);
            left: 50%;
            width: 80%;
            height: 60%;
            background-color: greenyellow;
        }
    </style>
</head>
<body>
  <h2>测试文本</h2>
  <button>获取远程数据</button>
<!--  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>-->
  <script src="./util/axios.min.js"></script>

  <div class="box-out">
      <div class="box-inner"></div>
      <div class="in"></div>
  </div>
  <script>
    let btn = document.querySelector('button')
    btn.addEventListener('click', function(){
      console.log('按钮点击成功!!!发送信息')
      axios('http://localhost:20315/test').then(response => {
          console.log(response.data)
      })
    })
  </script>
  <button class="dispatch">dispatchEvent</button>
<!-- 滑动验证 -->
  <script>
      let dispatch = document.querySelector('.dispatch')
      let minSize = 5 // 误差多少px
      let sideOut = document.querySelector('.box-out')
      let sideInner = document.querySelector('.box-inner')
      let inDom = document.querySelector('.in')
      var afterStyle = window.getComputedStyle(sideOut, "::after");

      sideInner.addEventListener('mousedown',(e) => {
          // console.log('触发鼠标点击事件',e)
          sideOut.addEventListener('mousemove',moveHandler)
      })

      function moveHandler(e){
          // console.log('鼠标移动事件',e)
          let abouX = (sideInner.style.width / 2)// 本身一半
          let targetX = e.x - 100 - abouX
          let outWidth = sideOut.style.width
          let innerWidth = sideInner.style.width
          // 可用拖动距离
          let canMoveX = 350
          let leftx = sideInner.style.left
          if (targetX > canMoveX) {
              inDom.innerText = '验证成功'
              console.log('滑块验证成功!!!!!!!!!!!!!!!')
              btn.click()
              return
          }
          sideInner.style.left = (targetX) + 'px'
          inDom.style.width = targetX + 'px'
          inDom.innerText = ''
      }
      window.addEventListener('mouseup',() => {
          sideOut.removeEventListener('mousemove',moveHandler)
      })
  </script>
</body>
</html>


jsDom自动化实现

const express = require('express')
const moment = require('moment')
const jsdom = require('jsdom')
const { JSDOM } = jsdom;
const axios = require('axios')
const router = express.Router()
const uuid = require('uuid')


function getHtml(targetPath){
    return  new Promise((resolve, reject) => {
        axios.get(targetPath).then(response => {
            resolve(response.data)
        }).catch(err => {
            reject(err)
        })
    })
}

getHtml('http://localhost:20315/public/index.html').then(val => {
        let template = val

        let jsDomInstance = new JSDOM(template,{
            runScripts: "dangerously",
            resources: "usable",
        })
        let {window} = jsDomInstance
        window.localStorage = storage;
        /* 插入axios脚本 */
        let axiosScript = window.document.createElement('script')
        axiosScript.src = 'https://unpkg.com/axios/dist/axios.min.js'
        window.document.body.appendChild(axiosScript)

        /* 插入js拖动校验脚本 */
        let moveScript = window.document.createElement('script')
        moveScript.text = `
                        /*创建event*/
                         let eve = sideInner.dispatchEvent(new MouseEvent('mousedown'),{
                             bubbles: false, // 默认值false, 事件是否冒泡
                             cancelable: false,  // 默认值false, 事件能否被取消
                             composed: false  // 默认值false, 事件是否会在影子DOM根节点之外触发侦听器。
                         })
                    
                    
                    
                          // 创建鼠标移动事件
                          let executionCount = 452 //执行次数
                          console.log('开始分发事件')
                          for(let i=100;i<=executionCount;i++) {
                              setTimeout(()=> {
                                  sideInner.dispatchEvent(new MouseEvent('mousemove',{
                                      bubbles: true,
                                      cancelable: true,
                                      view: window,
                                      clientX: i,
                                      clientY: 181,
                                  }))
                                  if(i == executionCount) {
                                      window.dispatchEvent(new MouseEvent('mouseup'),{})
                                      console.log('解除自动分发事件')
                                  }
                              },i*3)
                          }
                        `
        window.document.body.appendChild(moveScript)
    })

执行效果

Alt text

可以看到已经通过验证并发起了请求

分类:随笔

标签:后端爬虫随笔

上一篇宝塔环境:Nginx下使用了cdn加速,获取用户真实IP下一篇puppeteer爬虫

版权声明

本文系作者 @黄粱一梦 转载请注明出处,文中若有转载的以及参考文章地址也需注明。\(^o^)/~

Preview