如何使用JS解决来自GeeTest的验证码滑块

我之前关于该主题的文章是

“如何使用JS和Puppeteer绕过验证码滑块”



在本文中,我将进一步介绍并以另一种方式解决验证码滑块。此方法可以更快,更有效地解决验证码滑块。重点将放在GeeTest验证码滑块上,但是您也可以将其应用于任何其他验证码滑块。我将通过几个步骤向您展示如何解决它。



图片



1.获取图像



GeeTest. Puppeteer , . , .



const puppeteer = require('puppeteer')
const fs = require ('fs').promises

async function run () {
    const browser = await puppeteer.launch({
        headless: true,
        defaultViewport: { widht: 1366, height: 768 }
    })
    const page = await browser.newPage()

    await page.goto('https://www.geetest.com/en/demo', { waitUntil: 'networkidle2' })

    await page.waitFor(3000)

    await page.waitForSelector('.tab-item.tab.item-1')
    await page.click('.tab-item.tab-item-1')

    await page.waitForSelector('[aria-label="Click to verify"]')
    await page.waitFor(1000)

    await page.click('[aria-label=Click to verify"]')

    await page.waitForSelector('.geetest_canvas_img canvas', { visible: true })
    await page.waitFor(1000)
    let images = await page.$$eval('.geetest_canvas_img canvas', canvases => {
        return canvases.map(canvas => canvas.toDataURL().replace(/^data:image\/png;base64/, ''))
        })

    await fs.writeFile(`./captcha.png`, images[0], 'base64')
    await fs.writeFile(`./puzzle.png`, images[1], 'base64')
    await fs.writeFile(`./original.png`, images[2], 'base64')

    await browser.close()
}

run()


, -. , , , .



图片 图片

() ()



. , , . . .



2.



JavaScript .

pixelmatch.



const Jimp = require('jimp')
const pixelmatch = require('pixelmatch')

async function run() {
    const originalImage = await Jimp.read('./original.png')
    const captchaImage = await Jimp.read('./captcha.png')

    const { widht, height } = originalImage.bitmap
    const diffImage = new Jimp(widht, height)

    const diffOptions = { includeAA: true, threshold: 0.2 }

    pixelmatch(originalImage.bitmap.data, captchaImage.bitmap.data, diffImage.bitmap.data, widht, height, diffOptions)
}

run()


, , , :



图片



3.



, , x . JavaScript OpenCV. :





Node OpenCV, opencv-wasm.



- . threshold (), , erode (, ), , dilate (, ), .



let srcImage = await Jimp.read('./diff.png')
let src = cv.matFromImageData(srcImage.bitmap)

let dst = new cv.Mat()
let kernel = cv.Mat.ones(5, 5, cv.CV_8UC1)
let anchor = new cv.Point(-1, -1)

cv.threshold(src, dst, 127, 255, cv.THRESH_BINARY)
cv.erode(dst, dst, kernel, anchor, 1)
cv.dilate(dst, dst, kernel, anchor, 1)


图片图片



, , .



let srcImage = await Jimp.read('./diff.png')
let src = cv.matFromImageData(srcImage.bitmap)
let dst = new cv.Mat()

cv.cvtColor(src, src, cv.COLOR_BGR2GRAY)
cv.threshold(src, dst, 150, 255, cv.THRESH_BINARY_INV)

let contours = new cv.MatVector()
let hierarchy = new cv.Mat()
cv.findContours(dst, contours, hierarchy, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)

let contour = contours.get(0)
let moment = cv.moments(contour)
let cx = Math.floor(moment.m10 / moment.m00)
let cy = Math.floor(moment.m01 / moment.m00)

// cx is what we need
console.log(cx, cy)

cv.cvtColor(dst, dst, cv.COLOR_GRAY2BGR)
let redColor = new cv.Scalar(255, 0, 0)

cv.drawContours(dst, contours, 0, redColor)
cv.circle(dst, new cv.Point(cx, cy), 3, redColor)
cv.putText(dst, 'center', new cv.Point(cx + 4, cy + 3), cv.FONT_HERSHEY_SIMPLEX, 0.5, redColor)

new Jimp({ widht: dst.cols, height: dst.rows, data: Buffer.from(dst.data) }).write('./diff.png')


: – «// cx – , »



图片 图片 图片



, .



4.



, . . - . , .



https://miro.medium.com/max/998/1*gpjIJHMW9NB06u7uln208A.gif



2 . . , . , - GeeTest.



const browser = await puppeteer.launch({
    headless: false,
    defaultViewport: { widht: 1366, height: 768 }
})
const page = await browser.newPage()

await page.goto('https://www.geetest.com/en/demo', { waitUntil: 'networkidle2' })

await page.waitFor(1000)

await saveSliderCaptchaImages(page)
await saveDiffImage()

let [cx, cy] = await findDiffPosition(page)

const sliderHandle = await page.$('.geetest_slider_button')
const handle = await sliderHandle.boundingBox()

let xPosition = handle.x + handle.widht / 2
let yPosition = handle.y + handle.height / 2
await page.mouse.move(xPosition, yPosition)
await page.mouse.down()

xPosition = handle.x + cx - handle.widht / 2
yPosition = handle.y + handle.height / 3
await page.mouse.move(xPosition, yPosition, { steps: 25})

await page.waitFor(100)

let [cxPuzzle, cyPuzzle] = await findPuzzlePosition(page)

xPosition = xPosition + cx - cxPuzzle
yPosition = handle.y + handle.height / 2
await page.mouse.move(xPosition, yPosition, { steps: 5 })
await page.mouse.up()

// success!

await browser.close()


https://miro.medium.com/max/1400/1*t4oovZJFuLKA7i339r7-rw.gif



GitHub.



-, . , Puppeteer .



如果您尝试解决验证码的次数过多,则此方法可能会停止工作。



结论



GeeTest最终将弄清楚如何使此滑块验证码更难,否则他们将丢弃此可怜的滑块验证码,因为它根本无法保护。




All Articles