Comparisons

Sticky-сессии или ротация IP: какую стратегию прокси выбрать под CAPTCHA

Если солвер вернул токен, а сайт всё равно отклонил форму, дело чаще не в решении, а в том, что между решением и отправкой сменился исходящий IP. Отсюда правило выбора:

  • одна пользовательская сессия — вход, чекаут, многошаговая форма — идёт с одного IP (sticky-режим);
  • независимые запросы — каталог, выдача, мониторинг цен — распределяются по пулу (ротация).

Ошибка стоит дважды: лишние проверки CAPTCHA и лишние потоки под них.

Матрица выбора: с чего начать

Сценарий Режим Причина
Вход, регистрация sticky токен сверяется с IP
Чекаут, оплата sticky cookie привязаны к IP
Форма с CAPTCHA sticky связка «токен — IP»
Сбор каталога ротация запросы независимы
Выдача, мониторинг цен ротация лимиты считаются по IP

Как устроены оба режима

Sticky-режим закрепляет адрес на заданный TTL, ротация выдаёт новый на каждый запрос.

STICKY SESSION:
  Request 1 ──▶ IP: 192.168.1.50 ──▶ site.com/login
  Request 2 ──▶ IP: 192.168.1.50 ──▶ site.com/dashboard
  Request 3 ──▶ IP: 192.168.1.50 ──▶ site.com/account
  (Same IP for all requests in the session window)

ROTATING SESSION:
  Request 1 ──▶ IP: 192.168.1.50 ──▶ site.com/page1
  Request 2 ──▶ IP: 10.0.0.77   ──▶ site.com/page2
  Request 3 ──▶ IP: 172.16.5.22 ──▶ site.com/page3
  (Different IP per request)

Сравнение по ключевым факторам

Доли проверок ниже — ориентировочные диапазоны, они зависят от площадки.

Фактор sticky ротация
IP на запрос тот же 1–30 мин новый каждый раз
Целостность сессии сохраняется отсутствует
Доля CAPTCHA: многошаговые сценарии 5–10 % 30–50 %
Доля CAPTCHA: отдельные страницы 10–20 % 5–10 %
Валидность токена выше, IP не меняется риск несоответствия
Скорость выше, соединение живёт ниже, новое соединение
Риск блокировки адреса выше, если адрес помечен ниже, адрес новый

Где sticky-сессия обязательна

Многошаговый вход с проверкой CAPTCHA

Проверка CAPTCHA сопоставляет токен с адресом, с которого пришёл запрос. Если IP изменился между решением и отправкой, токен отклоняется, а в панели управления решение при этом числится успешным. Отсюда правило: страница, решение и отправка идут из одной requests.Session() с одним прокси.

import requests
import time

CAPTCHAAI_KEY = "YOUR_API_KEY"
CAPTCHAAI_URL = "https://ocr.captchaai.com"

# Sticky session: same IP for the entire login flow
STICKY_PROXY = {
    "http": "http://user-session-abc123:[email protected]:5000",
    "https": "http://user-session-abc123:[email protected]:5000",
}

session = requests.Session()
session.proxies = STICKY_PROXY
session.headers.update({
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
    "AppleWebKit/537.36 Chrome/126.0.0.0 Safari/537.36",
})


def login_with_captcha(url, sitekey, username, password):
    """Full login flow — MUST use same IP throughout."""
    # Step 1: Load login page (IP: 192.168.1.50)
    session.get(url)

    # Step 2: Solve CAPTCHA (solved against IP: 192.168.1.50)
    token = solve_recaptcha(sitekey, url)

    # Step 3: Submit login (MUST be IP: 192.168.1.50)
    resp = session.post(url, data={
        "username": username,
        "password": password,
        "g-recaptcha-response": token,
    })

    return resp.status_code == 200


def solve_recaptcha(sitekey, pageurl):
    resp = requests.post(f"{CAPTCHAAI_URL}/in.php", data={
        "key": CAPTCHAAI_KEY,
        "method": "userrecaptcha",
        "googlekey": sitekey,
        "pageurl": pageurl,
        "json": 1,
    })
    task_id = resp.json()["request"]

    for _ in range(60):
        time.sleep(5)
        result = requests.get(f"{CAPTCHAAI_URL}/res.php", params={
            "key": CAPTCHAAI_KEY, "action": "get",
            "id": task_id, "json": 1,
        })
        data = result.json()
        if data["request"] != "CAPCHA_NOT_READY":
            return data["request"]

    raise TimeoutError("CAPTCHA solve timeout")

Почему токен теряет силу при смене IP

Sticky session:

  1. Browser loads page    → IP: 5.5.5.5
  2. CAPTCHA solved        → Token bound to session
  3. Form submitted        → IP: 5.5.5.5 ✅ Token accepted

Rotating session:

  1. Browser loads page    → IP: 5.5.5.5
  2. CAPTCHA solved        → Token bound to session
  3. Form submitted        → IP: 9.9.9.9 ❌ Token may be rejected

Так же ведёт себя Cloudflare Turnstile: токен cf-turnstile-response живёт недолго и рассчитан на ту же сессию.

Где выигрывает ротация IP

Массовый сбор страниц каталога

Состояния между запросами нет: пятьсот карточек с одного IP — всплеск частоты, те же пятьсот через пул — фон.

import concurrent.futures

ROTATING_PROXY = {
    "http": "http://user:[email protected]:5000",
    "https": "http://user:[email protected]:5000",
}


def scrape_page(url):
    """Each request gets a fresh IP automatically."""
    resp = requests.get(
        url,
        proxies=ROTATING_PROXY,
        headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"},
        timeout=30,
    )
    if resp.status_code == 200:
        return url, resp.text
    return url, None


urls = [f"https://example.com/products?page={i}" for i in range(1, 501)]

with concurrent.futures.ThreadPoolExecutor(max_workers=10) as pool:
    results = list(pool.map(scrape_page, urls))
    success = sum(1 for _, html in results if html)
    print(f"Scraped: {success}/{len(urls)}")

Парсинг поисковой выдачи

Поисковые системы считают объём запросов по адресу: постраничный сбор выдачи — случай для ротации и пауз.

def scrape_search(query, pages=10):
    results = []
    for page in range(pages):
        # Each request → new IP
        resp = requests.get(
            "https://search-engine.example.com/search",
            params={"q": query, "start": page * 10},
            proxies=ROTATING_PROXY,
            timeout=20,
        )
        results.append(resp.text)
        time.sleep(2)
    return results

Собирайте только те данные, которые вы вправе обрабатывать: для аудитории в РФ это вопрос 152-ФЗ, для трансграничных проектов — та же логика GDPR.

Гибридная схема в одном пайплайне

На практике оба режима живут рядом: один менеджер прокси выдаёт закреплённый адрес под авторизацию, ротируемый — под остальное.

class HybridProxyManager:
    """Sticky for multi-step flows, rotating for single requests."""

    def __init__(self, provider_host, username, password, port=5000):
        self.host = provider_host
        self.username = username
        self.password = password
        self.port = port

    def get_rotating_proxy(self):
        return {
            "http": f"http://{self.username}:{self.password}@{self.host}:{self.port}",
            "https": f"http://{self.username}:{self.password}@{self.host}:{self.port}",
        }

    def get_sticky_proxy(self, session_id, duration_min=10):
        sticky_user = f"{self.username}-session-{session_id}-ttl-{duration_min}"
        return {
            "http": f"http://{sticky_user}:{self.password}@{self.host}:{self.port}",
            "https": f"http://{sticky_user}:{self.password}@{self.host}:{self.port}",
        }


proxy_mgr = HybridProxyManager("proxy.example.com", "user", "pass")


def scrape_with_auto_strategy(url, needs_login=False):
    if needs_login:
        # Multi-step → sticky
        import uuid
        session_id = uuid.uuid4().hex[:8]
        proxy = proxy_mgr.get_sticky_proxy(session_id)
    else:
        # Single page → rotating
        proxy = proxy_mgr.get_rotating_proxy()

    return requests.get(url, proxies=proxy, timeout=30)

Тот же подход на Node.js

const axios = require("axios");

const CAPTCHAAI_KEY = "YOUR_API_KEY";
const PROXY_HOST = "proxy.example.com";
const PROXY_PORT = 5000;

function getProxy(mode, sessionId = null) {
  const user =
    mode === "sticky" ? `user-session-${sessionId}` : "user";

  return {
    proxy: {
      host: PROXY_HOST,
      port: PROXY_PORT,
      auth: { username: user, password: "pass" },
    },
  };
}

// Rotating: bulk scrape
async function scrapePages(urls) {
  const results = [];
  for (const url of urls) {
    const config = getProxy("rotating");
    const resp = await axios.get(url, { ...config, timeout: 30000 });
    results.push({ url, data: resp.data });
  }
  return results;
}

// Sticky: login flow
async function loginFlow(loginUrl, sitekey, credentials) {
  const sessionId = Date.now().toString(36);
  const config = getProxy("sticky", sessionId);

  // Step 1: Load page
  await axios.get(loginUrl, config);

  // Step 2: Solve CAPTCHA
  const submitResp = await axios.post(
    "https://ocr.captchaai.com/in.php",
    null,
    {
      params: {
        key: CAPTCHAAI_KEY,
        method: "userrecaptcha",
        googlekey: sitekey,
        pageurl: loginUrl,
        json: 1,
      },
    }
  );
  const taskId = submitResp.data.request;

  let token;
  for (let i = 0; i < 60; i++) {
    await new Promise((r) => setTimeout(r, 5000));
    const res = await axios.get("https://ocr.captchaai.com/res.php", {
      params: { key: CAPTCHAAI_KEY, action: "get", id: taskId, json: 1 },
    });
    if (res.data.request !== "CAPCHA_NOT_READY") {
      token = res.data.request;
      break;
    }
  }

  // Step 3: Submit with same IP
  return axios.post(
    loginUrl,
    {
      ...credentials,
      "g-recaptcha-response": token,
    },
    config
  );
}

Какой TTL держать для sticky-сессии

TTL Под какие задачи На что смотреть
1 мин отправка одной формы истекает посреди потока
5 мин вход и пара действий рабочий баланс
10 мин многостраничные сценарии умеренный риск
30 мин длинные сессии просмотра риск выше
60 мин долгие операции в кабинете самый высокий риск

Два ориентира при выборе значения:

  • берите TTL с двукратным запасом к времени сценария;
  • учитывайте сетевое плечо: у команды в Алматы, работающей через европейский регион, каждый шаг дороже по RTT, и на нестабильном канале запас важнее экономии.

Диагностика: что ломается на практике

Симптом Причина Что сделать
токен получен, форма его отклоняет IP сменился между шагами перевести сценарий в sticky
много CAPTCHA на независимых страницах один адрес собирает слишком много URL перевести сбор на ротацию
cookie теряются посреди сценария TTL sticky-сессии истёк увеличить TTL
ротация медленнее ожидаемого новое соединение на каждый адрес пул соединений, keep-alive
адрес в sticky-сессии заблокирован весь трафик идёт через один IP уменьшить TTL, добавить паузы

Как режим сессии отражается на бюджете

Провайдеру прокси вы платите одинаково в обоих режимах — разницу создаёт число проверок CAPTCHA. Тарификация CaptchaAI идёт за одновременные потоки, а не за каждое решение. BASIC ($15/мес, 5 потоков) закрывает разовые сценарии авторизации, ADVANCE ($90/мес, 50 потоков) — регулярный сбор с постоянным фоном проверок.

Отсюда два следствия:

  • неверный режим не увеличивает счёт напрямую — он увеличивает поток задач и заставляет держать тариф выше ради той же скорости;
  • фиксированный платёж за потоки предсказуем для команд, которые зарабатывают в местной валюте, а платят в долларах.

Частые вопросы

Как понять, что виноват режим прокси, а не сама проверка CAPTCHA?

  1. Логируйте исходящий адрес на каждом шаге сценария.
  2. Сверьте ответ res.php с ответом формы.
  3. Если токен получен, а форма вернула ошибку валидации, причина в смене IP или в истёкшем токене.

Какой TTL ставить, если решение CAPTCHA занимает 20–30 секунд?

Удваивайте полное время сценария вместе с ожиданием: обычный вход — 5 минут, несколько форм подряд — 10. Ставить 60 минут «на всякий случай» не стоит.

Растёт ли расход CaptchaAI при переходе на ротацию?

Прямой связи нет: тарификация идёт по потокам, а не по числу решений. Растёт число задач, если ротацию включили там, где нужна была сессия.

Нужны ли резидентные прокси или хватит дата-центровых?

Это отдельный от режима вопрос: дата-центровые адреса дешевле и быстрее на публичных страницах, резидентные ровнее проходят там, где площадка чувствительна к происхождению трафика.

Смежные материалы

Настройки sticky-сессий у провайдеров: Bright Data, Smartproxy, Oxylabs.

Подберите режим сессии под свой сценарий — получите API-ключ CaptchaAI и подключите решение CAPTCHA к обоим режимам.

Комментарии для этой статьи отключены.