Как автоматически решать слайдер капчу
Слайдер капча блокирует трафик от автоматических ботов, что приводит к проблемам доступностью сайтов и создает трудности при тестировании проектов.
Статья описывает процесс взаимодействия с API сервиса для реализации решения по автоматическому обходу слайдеров.
Обход слайдер капчи
Разработали пример. Пример кода создан для того, чтобы показать, как можно использовать API для обхода разных пользовательских капч в формате слайдера.
ruCaptcha - это сервис для автоматического обхода любых слайдер капч.
Решение
Чтобы решить обойти слайдер, нужно рассчитать путь, по которому мы должны перетащить слайдер. В большинстве случаев нужно всего две точки: начало и конец, причем точка начала обычно статична, так что мы можем найти ее всего один раз. Вторую точку могут найти работники, мы можем показать им изображение и дать инструкции, описывающие, какую именно точку им нужно указать, они нажмут на эту точку, и API вернет координаты этой точки. Нужный метод API описан в документации, называется Coordinates.
Подход
Для взаимодействия с капчей мы должны использовать браузер и фреймворк, позволяющий управлять браузером. В этом примере мы будем использовать Puppeteer в качестве фреймворка. Также нужен @2captcha/captcha-solver для взаимодействия с API сервиса.
Подготовка
Установка:
yarn add puppeteer @2captcha/captcha-solver
Установите ключ API:
export APIKEY=your_api_key_here
Пример кода
Поскольку в коде мы используем операторы ES6 import
, давайте добавим следующее свойство в файл package.json
:
"type": "module"
Создайте файл с именем index.js
и приступите к добавлению кода:
Прежде всего, импортируем зависимости
import puppeteer from 'puppeteer'
import { Solver } from '@2captcha/captcha-solver'
import { readFile } from 'node:fs/promises'
Затем давайте создадим новый экземпляр Solver
с нашим ключом API
const solver = new Solver(process.env.APIKEY)
Нам понадобится сгенерировать несколько случайных чисел, поэтому добавим простую однострочную строку, которая выполнит эту задачу
const randomInt = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
Остальной код будет завернут в самоисполняющуюся async-функцию, так как гораздо удобнее вызывать все Promise-based методы Puppeeter с помощью async/await
.
(async () => {
// the rest of the code
})();
Запустим браузер, откроем открытую вкладку и откроем демонстрационную страницу captcha. Мы также определим переменную success
, которая будет хранить состояние процесса обхода капчи.
const browser = await puppeteer.launch({
devtools: true,
slowMo: 11
})
const [page] = await browser.pages()
await page.goto('https://www.jqueryscript.net/demo/image-puzzle-slider-captcha/')
let success = false
Никогда нет 100% гарантии, что мы обойдем капчу с первой попытки, поэтому давайте запустим цикл. Мы выйдем из цикла, когда капча будет успешно разгадана.
Демо-страница показывает модальное окно согласия на использование cookies для некоторых стран, поэтому давайте откажемся от всех cookies.
Также загрузим инструкции, которые показаны работникам.
while (!success) {
try {
const consentButton = await page.waitForSelector('body > div.fc-consent-root > div.fc-dialog-container > div.fc-dialog.fc-choice-dialog > div.fc-footer-buttons-container > div.fc-footer-buttons > button.fc-button.fc-cta-do-not-consent.fc-secondary-button', { timeout: 3000 })
if (consentButton) consentButton.click()
} catch (e) { }
const instruction = await readFile('./imginstructions.png', { encoding: 'base64' })
Далее нужно захватить изображение капчи и отправить его в API с помощью метода Coordinates. Есть вероятность, что изображение не загрузится, поэтому мы проверяем длину возвращаемого URL-адреса данных.
Получив изображение, мы передаем его в соответствующий метод экземпляра Solver
.
Результат содержит массив координат точек. В нашем случае должна быть только одна точка. Мы используем ее координату x
как расстояние между левой границей изображения и центром целевого фрагмента головоломки.
const img = await page.evaluate(() => document.querySelector('canvas').toDataURL())
if (img.length < 2000) return
try {
const res = await solver.coordinates({
body: img,
textinstructions: 'Puzzle center | Центр пазла',
imginstructions: instruction
})
const offset = res.data[0].x
Затем мы получим элемент слайдера, его координаты и размеры. Мы будем использовать его центр в качестве отправной точки для перетаскивания.
const slider = await page.$('div.slider')
const bb = await slider.boundingBox()
const init = {
x: bb.x + bb.width / 2,
y: bb.y + bb.height / 2
}
Затем вычисляем координаты конечной точки. В нашем случае ширина квадратной части головоломки составляет 40px, поэтому нам нужно вычесть половину этой ширины, так как мы ожидаем получить центр головоломки. Мы также используем полученную координату y
, чтобы избежать перемещения указателя только по горизонтали, так как мы знаем, что капча отслеживает путь.
const target = {
x: bb.x + bb.width / 2 + parseFloat(offset) - 20,
y: res.data[0].y
}
Опционально можно нарисовать небольшую рамку на изображении, чтобы увидеть точное место, на которое нажал работник.
await page.evaluate((coord) => {
console.log(coord)
const canvas = document.querySelector('#captcha > canvas')
let ctx = canvas.getContext('2d')
ctx.globalAlpha = 1
ctx.fillStyle = 'red'
ctx.fillRect(coord.x, coord.y, 3, 3)
}, {
x: parseInt(res.data[0].x),
y: parseInt(res.data[0].y)
})
Затем мы перемещаем указатель мыши в начальную точку, нажимаем и удерживаем кнопку мыши и перемещаем указатель в конечную точку. При перемещении ползунка мы задаем случайное количество шагов, чтобы сделать путь более сложным, поскольку капча отслеживает события мыши.
await page.mouse.move(init.x, init.y)
await page.mouse.down()
await page.mouse.move(target.x, target.y, {
steps: randomInt(50, 100)
})
await page.mouse.up()
Наконец, мы пытаемся понять, удалось ли нам обойти капчу. В нашем случае после решения мы перенаправляемся на другую страницу, поэтому ждем навигации. В случае успешного решения мы выходим из цикла, устанавливая переменную success
в true
, сообщаем о правильном ответе в API, делаем скриншот и закрываем страницу и браузер. В случае ошибки (отсутствие навигации в течение 5 секунд) мы сообщаем о неправильном ответе и делаем еще одну попытку решить капчу.
try {
await page.waitForNavigation({ timeout: 5000 })
success = true
await solver.goodReport(res.id)
await page.screenshot({
path: 'screenshot.png'
})
await new Promise(ok => setTimeout(() => ok(), 5000))
await page.close()
await browser.close()
} catch (e) {
await solver.badReport(res.id)
}
Как вы могли заметить, код, начинающийся с взаимодействия с API, завернут в блок try/catch
, поэтому нам нужно закрыть этот блок с помощью catch
, а также закрыть наш цикл здесь.
} catch (e) {
console.log(`Failed to solve the captcha: ${e.err}`)
}
}
Using this demo
Вы можете просто клонировать репозиторий, установить зависимости и запустить:
git clone [email protected]:2captcha/custom-slider-demo.git
yarn #or npm i
yarn start #or npm start
Это образовательный контент, не забывайте об этике при использовании сервиса.
Полезные материалы
Отвечаем на вопросы
Если после прочтения документации у вас остались вопросы, будем рады ответить и помочь:
Можно задать вопрос любым способом:
- Отправить письмо [email protected]
- Skype
- Создать тикет