Логотип «2Captcha»Перейти на главную страницу
Туториалы по обходу капчи

Эта статья была полезной?

Решение 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 Copy
pip install selenium requests

Пошаговая реализация

Шаг 1: Инициализация WebDriver

python Copy
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 Copy
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 Copy
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 Copy
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 Copy
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 Copy
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 Copy
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 Copy
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}")

Полезные ссылки

Заключение

Автоматизация reCAPTCHA v2 Image Grid сводится к грамотному извлечению состояния DOM через Canvas и правильной конвертации индексов при клике. Приведенный код учитывает динамическую подмену плиток 3x3, что является самой частой причиной ошибок. Начните с тестового запуска на демо-странице, проверьте логи извлечения base64, и интеграция займет минимум времени.