Эта статья была полезной?
Решение reCAPTCHA v2 Image Grid через Selenium
Технический специалист
Введение
Раньше я думал, что автоматизация reCAPTCHA v2 с сеткой изображений — это головная боль из-за динамической подгрузки плиток и необходимости применять компьютерное зрение. Оказалось, что комбинация Selenium и Grid API 2Captcha решает эту задачу за считанные секунды.
В этом гайде я покажу, как извлечь данные капчи через Canvas API, отправить их работникам платформы и программно кликнуть по нужным изображениям, обойдя все типичные подводные камни.
Как это работает
reCAPTCHA v2 Image Grid предлагает сетку 3x3 или 4x4 и просит выбрать изображения по категории. Вместо сложного OCR мы используем JavaScript (Canvas API) прямо в браузере, чтобы «сшить» все плитки в одно изображение base64. Этот файл отправляется в Grid API, а полученные индексы используются для кликов через Selenium.
Подготовка окружения
Для работы вам понадобятся:
- Python 3.7 или выше
- Selenium WebDriver (например, ChromeDriver)
- Библиотека Requests
- API-ключ от 2Captcha
Установите необходимые зависимости:
bash
pip install selenium requests
Пошаговая реализация
Шаг 1: Инициализация WebDriver
python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
import requests
driver = webdriver.Chrome()
driver.maximize_window()
Шаг 2: Вызов капчи на целевом сайте
python
driver.get("https://example.com")
wait = WebDriverWait(driver, 10)
checkbox = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, ".recaptcha-checkbox-border")))
checkbox.click()
# Ожидание появления сетки
time.sleep(2)
Шаг 3: Извлечение данных через Canvas API
Эта функция получает текст задания и объединяет изображения сетки в один base64-объект, корректно обрабатывая подмену отдельных плиток в сетке 3x3.
python
def extract_captcha_data(driver):
try:
instruction = driver.find_element(By.CSS_SELECTOR, ".rc-imageselect-desc-wrapper")
comment = instruction.text.replace('\n', ' ')
except Exception:
comment = "Select all matching images"
# Проверка сетки 4x4
try:
img_4x4 = driver.find_element(By.CSS_SELECTOR, "img.rc-image-tile-44")
canvas_data = driver.execute_script("""
var img = arguments[0];
var canvas = document.createElement('canvas');
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
return canvas.toDataURL().replace(/^data:image\/[A-z]*;base64,/, '');
""", img_4x4)
return {"body": canvas_data, "comment": comment, "rows": 4, "columns": 4}
except Exception:
pass
# Обработка сетки 3x3 с учетом обновленных плиток
try:
table_3x3 = driver.find_element(By.CSS_SELECTOR, "table.rc-imageselect-table-33 tbody")
initial_img = table_3x3.find_element(By.CSS_SELECTOR, "img.rc-image-tile-33")
updated_tiles = driver.find_elements(By.CSS_SELECTOR, "img.rc-image-tile-11")
canvas_data = driver.execute_script("""
var initialImg = arguments[0];
var updatedTiles = arguments[1];
var canvas = document.createElement('canvas');
canvas.width = initialImg.naturalWidth;
canvas.height = initialImg.naturalHeight;
var ctx = canvas.getContext('2d');
ctx.drawImage(initialImg, 0, 0);
if (updatedTiles.length > 0) {
var positions = [
{x: 0, y: 0}, {x: canvas.width/3, y: 0}, {x: canvas.width/3*2, y: 0},
{x: 0, y: canvas.height/3}, {x: canvas.width/3, y: canvas.height/3}, {x: canvas.width/3*2, y: canvas.height/3},
{x: 0, y: canvas.height/3*2}, {x: canvas.width/3, y: canvas.height/3*2}, {x: canvas.width/3*2, y: canvas.height/3*2}
];
updatedTiles.forEach(function(tile) {
var index = tile.parentElement.parentElement.parentElement.tabIndex - 3;
if (index >= 1 && index <= 9) {
ctx.drawImage(tile, positions[index-1].x, positions[index-1].y);
}
});
}
return canvas.toDataURL().replace(/^data:image\/[A-z]*;base64,/, '');
""", initial_img, updated_tiles)
return {"body": canvas_data, "comment": comment, "rows": 3, "columns": 3}
except Exception as e:
raise Exception(f"Не удалось извлечь данные капчи: {e}")
Шаг 4: Отправка в Grid API (API v2)
python
def solve_captcha_via_api(captcha_data, api_key):
api_url = "https://api.2Captcha.com/createTask"
payload = {
"clientKey": api_key,
"task": {
"type": "GridTask",
"body": captcha_data["body"],
"comment": captcha_data["comment"],
"rows": captcha_data["rows"],
"columns": captcha_data["columns"]
}
}
response = requests.post(api_url, json=payload)
result = response.json()
if result.get("errorId") != 0:
raise Exception(f"Ошибка API: {result.get('errorDescription', 'Неизвестная ошибка')}")
task_id = result.get("taskId")
get_result_url = "https://api.2Captcha.com/getTaskResult"
while True:
time.sleep(5)
result_response = requests.post(
get_result_url,
json={"clientKey": api_key, "taskId": task_id}
)
result_data = result_response.json()
if result_data.get("status") == "ready":
# API v2 возвращает массив целых чисел, например [1, 4, 7]
return result_data["solution"]["click"], task_id
if result_data.get("status") == "processing":
continue
raise Exception(f"Ошибка API: {result_data.get('errorDescription')}")
Шаг 5: Обработка кликов
Важно: API возвращает индексы, начиная с 1, а селекторы в DOM работают с индексами, начиная с 0.
python
def click_on_images(driver, indices):
tasks = driver.find_elements(By.CSS_SELECTOR, "div.rc-imageselect-tile")
for index in indices:
if 1 <= index <= len(tasks):
tasks[index - 1].click()
time.sleep(0.3) # Задержка для имитации действий человека
try:
verify_button = driver.find_element(By.CSS_SELECTOR, ".rc-button-default")
verify_button.click()
except Exception:
pass # Кнопка может отсутствовать при включенной автопроверке
Шаг 6: Полный цикл использования
python
def main():
API_KEY = "ВАШ_API_КЛЮЧ"
try:
driver = webdriver.Chrome()
driver.maximize_window()
driver.get("https://example.com")
wait = WebDriverWait(driver, 10)
checkbox = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, ".recaptcha-checkbox-border")))
checkbox.click()
time.sleep(2)
captcha_data = extract_captcha_data(driver)
print(f"Извлечены данные: сетка {captcha_data['rows']}x{captcha_data['columns']}")
solution_indices, task_id = solve_captcha_via_api(captcha_data, API_KEY)
print(f"Решение от работников: {solution_indices}")
click_on_images(driver, solution_indices)
time.sleep(5)
except Exception as e:
print(f"Ошибка: {e}")
finally:
input("Нажмите Enter, чтобы закрыть браузер...")
driver.quit()
if __name__ == "__main__":
main()
Параметры запроса (createTask)
| Параметр | Тип | Обязателен | Описание |
|---|---|---|---|
clientKey |
Строка | Да | Ваш API-ключ |
type |
Строка | Да | Тип задачи: GridTask |
body |
Строка | Да | Изображение в формате base64 |
comment |
Строка | Да | Текст задания на английском языке |
rows |
Число | Да | Количество строк (3 или 4) |
columns |
Число | Да | Количество столбцов (3 или 4) |
Решение частых проблем
Сетка не появляется
Убедитесь, что чекбокс был успешно нажат. Используйте явное ожидание:
python
WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.CSS_SELECTOR, ".rc-imageselect-table-33, .rc-image-tile-44"))
)
Неверный выбор изображений
Проверьте логику конвертации индексов: API возвращает значения от 1, а списки в Python/JS начинаются с 0. Формула index - 1 в коде выше решает эту задачу.
Ошибка извлечения Canvas
Убедитесь, что страница полностью загрузилась. Увеличьте время ожидания перед вызовом extract_captcha_data или проверьте селекторы в DevTools целевого сайта.
Отчеты о корректности
Если сайт отклонил решение, сообщите об этом для возврата средств. Если решение прошло успешно, отправьте положительный отчет.
python
def report_incorrect(task_id, api_key):
requests.get(f"https://2Captcha.com/res.php?key={api_key}&action=reportbad&id={task_id}")
def report_correct(task_id, api_key):
requests.get(f"https://2Captcha.com/res.php?key={api_key}&action=reportgood&id={task_id}")
Полезные ссылки
- Документация Grid API: https://2Captcha.com/api-docs/grid
- Репозиторий с полным примером на Selenium
- Песочница для тестирования: https://rucaptcha.com/setting#sandbox
Заключение
Автоматизация reCAPTCHA v2 Image Grid сводится к грамотному извлечению состояния DOM через Canvas и правильной конвертации индексов при клике. Приведенный код учитывает динамическую подмену плиток 3x3, что является самой частой причиной ошибок. Начните с тестового запуска на демо-странице, проверьте логи извлечения base64, и интеграция займет минимум времени.