CaptchaAI вернул индексы ячеек вида [1, 3, 6, 9], а клики всё равно проходят мимо нужных плиток? Дело почти никогда не в самом решении — сервис уже сказал, какие ячейки выбрать. Проблема почти всегда в шаге между ответом API и кликом: индекс ячейки нужно правильно перевести в пиксельные координаты внутри конкретного элемента сетки на странице.
Ниже — весь процесс целиком: как захватить сетку 3×3 или 4×4 из задания reCAPTCHA v2, отправить изображение в CaptchaAI, перевести возвращённые индексы в координаты клика и обработать случай, когда плитки меняются после первого раунда.
Прежде чем начинать, проверьте, что под рукой есть:
- API-ключ CaptchaAI (
YOUR_API_KEY) - Selenium или Puppeteer с доступом к iframe reCAPTCHA
- Стандартные средства работы с base64-изображениями (входят в Python и Node.js «из коробки»)
Два формата сетки: 3×3 и 4×4
CAPTCHA с изображением сетки встречается в одном из двух стандартных макетов:
3×3 Grid: 4×4 Grid:
1 2 3 1 2 3 4
4 5 6 5 6 7 8
7 8 9 9 10 11 12
13 14 15 16
Ячейки нумеруются слева направо и сверху вниз — как при обычном чтении. Тот же порядок использует CaptchaAI в ответе.
Коротко:
- 3×3 — 9 ячеек, индексы 1–9
- 4×4 — 16 ячеек, индексы 1–16
- Нумерация всегда начинается с 1, а не с 0
Шаг 1: захватите изображение сетки
Понадобятся сам файл сетки и текст инструкции («Выберите все квадраты со светофорами») — оба значения нужны на следующем шаге. Ниже — оба варианта, выберите свой стек:
- Selenium — если проект уже на Python
- Puppeteer — если автоматизация написана на Node.js
Совет: сохраните исходный
img_src— скриншот черезscreenshot_as_base64не хранит прямую ссылку на файл, а она пригодится, если сетку нужно будет сверить вручную.
Python (Selenium)
import base64
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
driver = webdriver.Chrome()
driver.get("https://example.com/form")
# Wait for reCAPTCHA iframe
WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.CSS_SELECTOR, "iframe[src*='recaptcha']"))
)
# Switch to challenge iframe
iframes = driver.find_elements(By.CSS_SELECTOR, "iframe[src*='recaptcha']")
challenge_iframe = iframes[-1] # Challenge iframe is typically the last one
driver.switch_to.frame(challenge_iframe)
# Get the grid image
grid_img = driver.find_element(By.CSS_SELECTOR, "img.rc-image-tile-33, img.rc-image-tile-44")
img_src = grid_img.get_attribute("src")
# Get instruction text
instruction = driver.find_element(
By.CSS_SELECTOR, ".rc-imageselect-desc-wrapper"
).text
print(f"Instruction: {instruction}")
# Screenshot the grid as base64
img_b64 = grid_img.screenshot_as_base64
# Determine grid size
classes = grid_img.get_attribute("class")
grid_size = "4x4" if "44" in classes else "3x3"
print(f"Grid size: {grid_size}")
driver.switch_to.default_content()
JavaScript (Puppeteer)
const puppeteer = require('puppeteer');
const fs = require('fs');
const browser = await puppeteer.launch({ headless: false });
const page = await browser.newPage();
await page.goto('https://example.com/form');
// Find the challenge iframe
const frames = page.frames();
const challengeFrame = frames.find(f => f.url().includes('recaptcha'));
// Get instruction
const instruction = await challengeFrame.$eval(
'.rc-imageselect-desc-wrapper',
el => el.textContent.trim()
);
// Screenshot the grid image
const gridImg = await challengeFrame.$('img.rc-image-tile-33, img.rc-image-tile-44');
const imgBuffer = await gridImg.screenshot();
const imgBase64 = imgBuffer.toString('base64');
// Determine grid size
const className = await challengeFrame.$eval(
'img.rc-image-tile-33, img.rc-image-tile-44',
el => el.className
);
const gridSize = className.includes('44') ? '4x4' : '3x3';
console.log(`Grid: ${gridSize}, Instruction: ${instruction}`);
Шаг 2: отправьте задачу в CaptchaAI
Инструкцию стоит свести к короткому ключевому слову — так CaptchaAI точнее понимает, что искать. Запрос на in.php включает:
key— ваш API-ключgrid_size—3x3или4x4img_type—recaptchainstructions— ключевое слово из инструкции
Результат забирается опросом res.php:
Опрашивайте
res.phpне чаще раза в 5 секунд — более частые запросы не ускоряют решение, а только расходуют лимит запросов на вашем тарифе.
import requests
import time
import json
API_KEY = "YOUR_API_KEY"
# Parse the instruction to a simple keyword
# "Select all images with traffic lights" → "traffic lights"
import re
keyword_match = re.search(r'(?:with|of|containing)\s+(.+?)\.?$', instruction, re.I)
keyword = keyword_match.group(1) if keyword_match else instruction
# Submit
with open("/tmp/grid.png", "wb") as f:
f.write(base64.b64decode(img_b64))
with open("/tmp/grid.png", "rb") as f:
resp = requests.post("https://ocr.captchaai.com/in.php",
files={"file": f},
data={
"key": API_KEY,
"method": "post",
"grid_size": grid_size,
"img_type": "recaptcha",
"instructions": keyword,
"json": "1",
}
).json()
if resp["status"] != 1:
raise Exception(f"Submit error: {resp['request']}")
task_id = resp["request"]
# Poll
for _ in range(20):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "get", "id": task_id, "json": "1"
}).json()
if result["status"] == 1:
cells = json.loads(result["request"])
print(f"Cells to click: {cells}") # e.g., [1, 3, 6, 9]
break
if result["request"] != "CAPCHA_NOT_READY":
raise Exception(f"Error: {result['request']}")
Шаг 3: переведите индексы ячеек в координаты клика
Ответ CaptchaAI — список индексов от 1 (например, [1, 3, 6, 9]). Прежде чем кликать, каждый индекс переводится в координаты центра ячейки:
Округление через
int()смещает клик максимум на пару пикселей — это в пределах допускаrc-imageselect-targetи на результат не влияет.
def cell_to_coordinates(cell_index, grid_size, grid_width, grid_height):
"""Convert a 1-based cell index to (x, y) center coordinates."""
if grid_size == "3x3":
cols, rows = 3, 3
else:
cols, rows = 4, 4
cell_w = grid_width / cols
cell_h = grid_height / rows
# Convert 1-based index to 0-based row/col
idx = cell_index - 1
col = idx % cols
row = idx // cols
# Center of the cell
x = col * cell_w + cell_w / 2
y = row * cell_h + cell_h / 2
return int(x), int(y)
# Example: grid is 300×300
for cell in cells:
x, y = cell_to_coordinates(cell, grid_size, 300, 300)
print(f"Cell {cell} → ({x}, {y})")
Для сетки 3×3 размером 300×300 пикселей функция вернёт:
Cell 1 → (50, 50)
Cell 3 → (250, 50)
Cell 6 → (250, 150)
Cell 9 → (250, 250)
Шаг 4: кликните по ячейкам
Перед нажатием verify проверьте:
- индексы соответствуют актуальному состоянию сетки (после подмены плиток — см. раздел ниже)
- клик приходится на центр ячейки, а не на её край
Пауза между кликами (
200 мсв примере Puppeteer) снижает шанс, что reCAPTCHA сочтёт серию кликов подозрительно быстрой.
Selenium
from selenium.webdriver.common.action_chains import ActionChains
driver.switch_to.frame(challenge_iframe)
# Get grid element position and size
grid_el = driver.find_element(By.CSS_SELECTOR, ".rc-imageselect-target")
grid_rect = grid_el.rect
grid_w = grid_rect["width"]
grid_h = grid_rect["height"]
actions = ActionChains(driver)
for cell in cells:
x, y = cell_to_coordinates(cell, grid_size, grid_w, grid_h)
# Click relative to grid element's top-left corner
actions.move_to_element_with_offset(
grid_el,
x - grid_w / 2, # offset from center
y - grid_h / 2
).click()
actions.perform()
# Click verify
verify_btn = driver.find_element(By.ID, "recaptcha-verify-button")
verify_btn.click()
driver.switch_to.default_content()
Puppeteer
// Click each cell by index
const tableRows = await challengeFrame.$$('table.rc-imageselect-table tr');
for (const cellIdx of cells) {
const row = Math.floor((cellIdx - 1) / (gridSize === '4x4' ? 4 : 3));
const col = (cellIdx - 1) % (gridSize === '4x4' ? 4 : 3);
const cell = (await tableRows[row].$$('td'))[col];
await cell.click();
await new Promise(r => setTimeout(r, 200));
}
await challengeFrame.click('#recaptcha-verify-button');
Сколько потоков нужно для сетки изображений
Тарификация идёт по потокам, а не по числу решённых сеток: одна задача — один поток на время решения, а решения внутри потока не ограничены. Примерный ориентир по нагрузке:
| Нагрузка | Подходящий план | Потоков |
|---|---|---|
| ~500 форм в день (QA-агентство, CI) | ADVANCE ($90/мес) | 50 |
| Несколько тысяч решений в день | PREMIUM ($170/мес) | 100 |
| Высоконагруженный сбор данных | CORPORATE ($240/мес) | 150 |
Цена не зависит от того, сколько ячеек в каждой сетке — только от числа параллельных запросов.
Что делать, если после клика появляются новые плитки
Некоторые сетки reCAPTCHA v2 подменяют выбранные плитки после каждого раунда. Надёжный способ — решать сетку заново в цикле, пока проверка не пройдёт:
Ограничивайте
max_rounds: reCAPTCHA v2 обычно не заменяет плитки больше 2–3 раз подряд, а дальнейшие неудачи чаще означают, что сетка не решается по другой причине.
def solve_with_dynamic_tiles(driver, api_key, max_rounds=3):
for round_num in range(max_rounds):
driver.switch_to.frame(challenge_iframe)
# Re-capture grid and instruction
img_b64 = driver.find_element(
By.CSS_SELECTOR, "img.rc-image-tile-33"
).screenshot_as_base64
# Submit and get cells (same as above)
cells = submit_and_poll(api_key, img_b64, "3x3", keyword)
if not cells:
break
# Click cells
click_cells(driver, cells, "3x3")
# Click verify
driver.find_element(By.ID, "recaptcha-verify-button").click()
driver.switch_to.default_content()
time.sleep(2)
# Check if solved (no more challenge iframe)
try:
driver.switch_to.frame(challenge_iframe)
driver.switch_to.default_content()
except Exception:
return True # Solved
return False
Частые ошибки и как их исправить
Типичные причины, с которыми сталкиваются при интеграции grid image CAPTCHA, и что с ними делать:
| Проблема | Причина | Решение |
|---|---|---|
| Возвращаются не те ячейки | Неверно указан grid_size |
Проверьте, действительно ли сетка 3×3 или 4×4 |
| Клики попадают мимо ячеек | Неправильное смещение координат | Сверьте реальные размеры элемента сетки |
ERROR_WRONG_FILE_EXTENSION |
Некорректный формат изображения | Отправляйте PNG или JPEG |
| После клика появляются новые плитки | Динамическая сетка | Решайте сетку заново после каждого раунда |
Часто задаваемые вопросы
Поддерживает ли CaptchaAI сетку изображений 4×4?
Да. Передайте grid_size=4x4 — в ответе будут индексы от 1 до 16 вместо 1–9.
Сколько стоит решение grid image CAPTCHA через API?
Тариф зависит от числа потоков: от BASIC ($15/мес, 5 потоков) до VIP-3 ($7,500/мес, 5000 потоков). Решения внутри потока не ограничены.
Что делать, если после первого клика появляются новые плитки?
Это штатное поведение динамической сетки reCAPTCHA v2. Захватите изображение и инструкцию заново, отправьте в CaptchaAI повторно и кликните по новым индексам.
Почему API возвращает ERROR_WRONG_FILE_EXTENSION?
CaptchaAI принимает только PNG и JPEG. WEBP или файл, повреждённый при кодировании в base64, отклоняется с этой ошибкой.
Можно ли использовать grid image API без Selenium или Puppeteer?
Да. CaptchaAI принимает любой валидный PNG/JPEG сетки независимо от того, как вы его получили — скриншот драйвера, прямая загрузка по img_src или файл, сохранённый вручную из DevTools.
Итог
- Захватите сетку и текст инструкции через Selenium или Puppeteer
- Отправьте изображение в CaptchaAI и дождитесь индексов ячеек
- Переведите индексы в пиксельные координаты центра каждой ячейки
- Кликните по ячейкам и нажмите verify — при динамической сетке повторите цикл
Решайте CAPTCHA с сеткой изображений через CaptchaAI
Зарегистрируйтесь на captchaai.com и получите API-ключ, чтобы подключить решение grid image к своему пайплайну уже сегодня.