Как настроить ротацию прокси для решения капчи в Python и Node

Ротацию прокси для капчи вы строите в собственном коде. Решатель берёт один прокси на задание и использует его только для этого задания, поэтому пул, политика ротации и логика повторов живут на вашей стороне. Два правила делают большую часть работы: закрепляйте один IP за одной сессией вместо смены прокси на каждом вызове и меняйте прокси только после сбоя. В этом руководстве разобраны точный формат прокси в каждом SDK, пара сырых API-запросов за ним и тот единственный тип капчи, где прокси молча игнорируется.
Прокси работают с тремя семействами капчи, а не со всеми четырьмя
Начнём с ограничения: оно экономит полдня отладки. CapSkip решает четыре семейства капчи, и прокси работают с тремя из них: reCAPTCHA, Turnstile и GeeTest. Капчи-картинки прокси не принимают.
reCAPTCHA здесь считается один раз. v2 с флажком, Invisible, Enterprise и v3 задаются параметрами одного и того же вызова решения, а не отдельными типами, поэтому с прокси они ведут себя одинаково.
| Тип | Поддержка прокси | Зачем |
|---|---|---|
| reCAPTCHA v2 и v3, включая Enterprise | Да | При решении выполняется запрос к Google через прокси |
| Cloudflare Turnstile | Да | То же самое, но к Cloudflare |
| GeeTest v3 | Да | То же самое, но к серверу API GeeTest |
| Изображение или OCR | Нет | Изображение уже на руках, поэтому исходящий запрос не выполняется |
Передача прокси в задание с картинкой ошибкой не считается. Он принимается и игнорируется, и это хуже, потому что баг в ротации там не даёт вообще никакого сигнала. Если вы направляете капчи-картинки через ту же обёртку, что и всё остальное, не передавайте аргумент с прокси на этом пути.
Формат прокси в каждом SDK
Каждый SDK принимает один и тот же объект из двух полей: тип и URI. URI содержит либо хост и порт, либо учётные данные, а за ними хост и порт.
# pip install capskip
from capskip import CapSkip
solver = CapSkip(host="127.0.0.1", port=8080)
# The proxy applies to this one solve, nothing is remembered.
result = solver.recaptcha(
sitekey="YOUR_SITEKEY",
url="https://example.com/page-with-recaptcha",
proxy={"type": "HTTPS", "uri": "user:[email protected]:3128"},
)
print(result["code"]) # token, solved through that IPNode принимает тот же объект в аргументе options.
// npm install capskip
const { CapSkip } = require('capskip');
const solver = new CapSkip({ host: '127.0.0.1', port: 8080 });
const result = await solver.turnstile(
'YOUR_SITEKEY',
'https://example.com/page-with-turnstile',
{ proxy: { type: 'SOCKS5', uri: '1.2.3.4:1080' } },
);
console.log(result.code, result.userAgent); // send both backPHP вкладывает его в массив options под ключом proxy, а .NET передаёт объект Proxy, собранный из тех же двух значений. Четыре допустимых значения типа везде одинаковы: HTTP, HTTPS, SOCKS5 и SOCKS5H. SOCKS5H разрешает DNS на стороне прокси, а не локально, и именно это нужно, когда целевой адрес резолвится по-разному в зависимости от региона.
Закрепляйте один прокси за сессией, а не за запросом
Первое побуждение состоит в том, чтобы брать новый IP на каждое решение. Это неверное поведение по умолчанию, и именно из-за него сессии попадают под подозрение.
Защищённая страница привязывает токен к тому контексту, в котором он был выдан. Если ваш краулер загружает страницу с одного IP, а токен решается с другого, сайт видит несоответствие между сессией просмотра и проверкой. Некоторые сайты на это не смотрят. Cloudflare и reCAPTCHA Enterprise смотрят, и токен либо признаётся низкокачественным, либо не проходит вовсе.
Значит, единицей ротации служит сессия, а не вызов. Один прокси загружает страницу, решает капчу и отправляет форму. Следующая сессия берёт следующий прокси.
# pip install capskip
import itertools
# One entry per exit IP. Round-robin, not random:
# random picks repeat, and a repeat is what a rate limiter sees.
POOL = [
{"type": "HTTPS", "uri": "user:[email protected]:3128"},
{"type": "HTTPS", "uri": "user:[email protected]:3128"},
{"type": "SOCKS5H", "uri": "user:[email protected]:1080"},
]
pool = itertools.cycle(POOL)
def new_session():
"""Hand the same proxy to the HTTP client and the solver."""
return next(pool)Круговой перебор (round-robin) выигрывает здесь у случайного выбора по одной причине: случайная выборка из небольшого пула достаточно часто повторяет один и тот же IP подряд, а идущие подряд запросы с одного выходного узла образуют как раз тот шаблон, за которым следят ограничители частоты запросов.
Меняйте прокси по факту сбоя, а не по таймеру
Второе правило касается того, когда переходить дальше. Ротация каждые N запросов выбрасывает рабочие IP и держит сломанные в обороте ещё до N вызовов. Меняйте прокси по фактическим признакам сбоя.
# pip install capskip
from capskip import CapSkip
from capskip import ApiException, NetworkException, TimeoutException
solver = CapSkip(host="127.0.0.1", port=8080)
def solve_with_retry(sitekey, url, attempts=3):
last = None
for _ in range(attempts):
proxy = new_session()
try:
return solver.recaptcha(sitekey=sitekey, url=url, proxy=proxy)
except (ApiException, NetworkException, TimeoutException) as err:
# Burn this IP for the run and take the next one.
last = err
raise lastПравильный потолок для большинства пулов составляет три попытки. Если sitekey не проходит на трёх разных IP, дело не в прокси: sitekey устарел, URL страницы неверный или сайт сменил тип задачи. Четвёртая попытка лишь потратит ресурс решения на подтверждение этого.
Какое исключение вы поймали, то и подсказывает, что произошло. NetworkException означает, что решатель был недоступен или задание опросили раньше, чем оно было готово. TimeoutException означает, что окно опроса истекло. ApiException несёт возвращённый код ошибки, и именно его стоит логировать вместе с прокси, потому что так вы находите тот самый мёртвый выходной узел в пуле из сорока.
Сырой API: proxy и proxytype
Без SDK то же самое делается двумя дополнительными полями формы в запросе на отправку. Объект из SDK ложится на них напрямую.
| Поле | Значение | По умолчанию |
|---|---|---|
| proxy | IP:PORT или login:pass@IP:PORT | Нет |
| proxytype | HTTP, HTTPS, SOCKS5 или SOCKS5H | HTTP |
# No install step. curl is already on your machine. curl -X POST http://127.0.0.1:8080/in.php \ -d "key=YOUR_API_KEY" \ -d "method=userrecaptcha" \ -d "googlekey=YOUR_SITEKEY" \ -d "pageurl=https://example.com/page-with-recaptcha" \ -d "proxy=user:[email protected]:3128" \ -d "proxytype=HTTPS" OK|2122988149 # submitted, and it will solve through that IP
По умолчанию proxytype равен HTTP, поэтому HTTPS- или SOCKS5-прокси, отправленный без этого поля, будет вызываться как обычный HTTP и не сработает. Отправляйте поле каждый раз, а не полагайтесь на то, что значение по умолчанию совпадёт с вашим пулом.
Замените хост, и тот же самый запрос будет работать с общим экземпляром. По умолчанию CapSkip в режиме Local слушает 127.0.0.1, а Настройки подключения также предлагают режим Server, в котором он слушает вашу сеть или публичный IP, чтобы другие машины могли обращаться к API. Разместите его на VPS, и один решатель будет обслуживать всех воркеров вашего парка парсеров. Работа с прокси не меняется: прокси по-прежнему задаётся для каждой задачи, а пул по-прежнему живёт в вашем собственном коде.
Частые ошибки и что они означают
| Симптом | Причина | Исправить |
|---|---|---|
| Капчи решаются, но токены отклоняются | Страница была загружена с другого IP, чем выполнено решение | Закрепите один прокси на загрузку, решение и отправку |
ERROR_CAPTCHA_UNSOLVABLE только на одном IP | Этот выходной узел заблокирован целевым сайтом | Уберите его из пула и повторите на следующем |
| Таймауты на каждом задании с прокси | proxytype не соответствует прокси | Отправляйте поле явно, а не полагайтесь на HTTP по умолчанию |
| Кажется, что прокси ничего не делает | Задание представляет собой капчу-картинку | Так и должно быть. Прокси работают с тремя другими семействами |
| Локально работает, а в контейнере падает | Внутри сети DNS разрешается иначе | Используйте SOCKS5H, чтобы имя хоста разрешал прокси |
Полные сведения о параметрах для каждого типа приведены в разделе Документация по API.
FAQ
Нужен ли вообще прокси, чтобы решать капчи?
Нет. Решение без прокси работает на большинстве сайтов, и это на одну движущуюся деталь меньше. Добавляйте прокси, когда цель ограничена по регионам, когда она лимитирует частоту запросов по IP или когда токены начинают отклоняться, хотя решаются чисто. Начните без прокси и добавьте его как исправление уже замеченной проблемы.
Резидентные или серверные прокси?
Серверные IP быстрее и дешевле, и их вполне хватает на сайтах, которые только ограничивают частоту запросов. Резидентные IP важны, когда цель оценивает саму сеть, а это частая история на страницах под Cloudflare и в reCAPTCHA Enterprise. Смешивать оба типа в одном пуле можно: сначала пробуйте серверные, при сбое переходите на резидентные.
В чём разница между SOCKS5 и SOCKS5H?
SOCKS5 разрешает имя хоста на вашей машине и отправляет прокси уже готовый IP. SOCKS5H отправляет само имя хоста и даёт прокси разрешить его. Используйте SOCKS5H, когда сайт возвращает разные адреса в зависимости от региона или когда ваш локальный резолвер вообще не видит цель.
Замедляет ли прокси решение капчи?
Немного, и зависит это скорее от выходного узла, чем от самого решателя. На сам опрос это не влияет: SDK начинают с 250 миллисекунд и увеличивают паузу до потолка pollingInterval, так что быстрое решение возвращается так же быстро. Медленный прокси проявляется как более долгий путь до первого успешного опроса, а не как лишние накладные расходы на опрос.
Коротко
Закрепляйте один прокси за сессией, меняйте его при сбое решения, а не по расписанию, всегда отправляйте proxytype и полностью пропускайте прокси в заданиях с картинками. Поскольку само решение выполняется локально, как безлимитный сервис распознавания капчи, повторная попытка не стоит ничего, кроме трафика прокси, и это делает ротацию по сбоям достаточно дешёвой, чтобы включить её по умолчанию. О более широком конвейере, частью которого это является, читайте решение капчи для парсинга, где работа с сессиями разобрана от начала до конца. А руководстве по интеграции с Python описывает настройку на стороне клиента.
