Назад к блогу

Как избежать блокировки при массовых запросах

Разбираем механизмы обнаружения автоматизации и конкретные техники защиты от блокировок при массовых запросах: от базовой ротации прокси до имитации человеческого поведения.

📅21 декабря 2025 г.

Защита от блокировок при массовых запросах: техники и инструменты

Блокировка аккаунтов и IP-адресов — главная проблема при парсинге, автоматизации и массовых операциях в социальных сетях. Современные антибот-системы анализируют десятки параметров: от частоты запросов до отпечатков браузера. В этом руководстве разберём конкретные механизмы обнаружения автоматизации и практические способы их обхода.

Механизмы обнаружения автоматизации

Современные системы защиты используют многоуровневый анализ для выявления ботов. Понимание этих механизмов критически важно для выбора правильной стратегии обхода.

Основные параметры анализа

IP-репутация: Антибот-системы проверяют историю IP-адреса, принадлежность к дата-центрам, наличие в чёрных списках. IP из известных прокси-пулов блокируются чаще.

Частота запросов (Request Rate): Человек физически не может отправлять 100 запросов в минуту. Системы анализируют не только общее количество, но и распределение во времени — равномерные интервалы между запросами выдают бота.

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

Технические отпечатки: User-Agent, заголовки HTTP, порядок заголовков, TLS-fingerprint, Canvas/WebGL fingerprinting. Несоответствия в этих параметрах — красный флаг для антибот-систем.

Параметр Что анализируется Риск обнаружения
IP-адрес Репутация, ASN, геолокация Высокий
User-Agent Версия браузера, ОС, устройство Средний
TLS Fingerprint Набор шифров, расширения Высокий
HTTP/2 Fingerprint Порядок заголовков, настройки Высокий
Canvas/WebGL Отрисовка графики Средний
Поведение Клики, скроллинг, время Высокий

Rate limiting и контроль частоты запросов

Контроль скорости отправки запросов — первая линия защиты от блокировок. Даже с ротацией прокси слишком агрессивный парсинг приведёт к бану.

Динамические задержки

Фиксированные интервалы (например, ровно 2 секунды между запросами) легко распознаются. Используйте случайные задержки с нормальным распределением:

import time
import random
import numpy as np

def human_delay(min_delay=1.5, max_delay=4.0, mean=2.5, std=0.8):
    """
    Генерация задержки с нормальным распределением
    имитирующей человеческое поведение
    """
    delay = np.random.normal(mean, std)
    # Ограничиваем диапазон
    delay = max(min_delay, min(delay, max_delay))
    
    # Добавляем микрозадержки для реалистичности
    delay += random.uniform(0, 0.3)
    
    time.sleep(delay)

# Использование
for url in urls:
    response = session.get(url)
    human_delay(min_delay=2, max_delay=5, mean=3, std=1)

Адаптивный rate limiting

Более продвинутый подход — адаптация скорости на основе ответов сервера. Если получаете коды 429 (Too Many Requests) или 503, автоматически снижайте темп:

class AdaptiveRateLimiter:
    def __init__(self, initial_delay=2.0):
        self.current_delay = initial_delay
        self.min_delay = 1.0
        self.max_delay = 30.0
        self.error_count = 0
        
    def wait(self):
        time.sleep(self.current_delay + random.uniform(0, 0.5))
        
    def on_success(self):
        # Постепенно ускоряемся при успешных запросах
        self.current_delay = max(
            self.min_delay, 
            self.current_delay * 0.95
        )
        self.error_count = 0
        
    def on_rate_limit(self):
        # Резко замедляемся при блокировке
        self.error_count += 1
        self.current_delay = min(
            self.max_delay,
            self.current_delay * (1.5 + self.error_count * 0.5)
        )
        print(f"Rate limit hit. New delay: {self.current_delay:.2f}s")

# Применение
limiter = AdaptiveRateLimiter(initial_delay=2.0)

for url in urls:
    limiter.wait()
    response = session.get(url)
    
    if response.status_code == 429:
        limiter.on_rate_limit()
        time.sleep(60)  # Пауза перед повтором
    elif response.status_code == 200:
        limiter.on_success()
    else:
        # Обработка других ошибок
        pass

Практический совет: Для разных сайтов оптимальная скорость различается. Крупные платформы (Google, Facebook) терпимы к 5-10 запросам в минуту с одного IP. Мелкие сайты могут блокировать уже при 20-30 запросах в час. Всегда начинайте консервативно и постепенно увеличивайте нагрузку, отслеживая процент ошибок.

Ротация прокси и управление IP-адресами

Использование одного IP-адреса для массовых запросов гарантирует блокировку. Ротация прокси распределяет нагрузку и снижает риск обнаружения.

Стратегии ротации

1. Ротация по запросам: Смена IP после каждого или каждых N запросов. Подходит для парсинга поисковых систем, где важна анонимность каждого запроса.

2. Ротация по времени: Смена IP каждые 5-15 минут. Эффективна для работы с социальными сетями, где важна стабильность сессии.

3. Sticky sessions: Использование одного IP для всей пользовательской сессии (авторизация, последовательность действий). Критично для сайтов с защитой от CSRF.

import requests
from itertools import cycle

class ProxyRotator:
    def __init__(self, proxy_list, rotation_type='request', rotation_interval=10):
        """
        rotation_type: 'request' (каждый запрос) или 'time' (по времени)
        rotation_interval: количество запросов или секунд
        """
        self.proxies = cycle(proxy_list)
        self.current_proxy = next(self.proxies)
        self.rotation_type = rotation_type
        self.rotation_interval = rotation_interval
        self.request_count = 0
        self.last_rotation = time.time()
        
    def get_proxy(self):
        if self.rotation_type == 'request':
            self.request_count += 1
            if self.request_count >= self.rotation_interval:
                self.current_proxy = next(self.proxies)
                self.request_count = 0
                print(f"Rotated to: {self.current_proxy}")
                
        elif self.rotation_type == 'time':
            if time.time() - self.last_rotation >= self.rotation_interval:
                self.current_proxy = next(self.proxies)
                self.last_rotation = time.time()
                print(f"Rotated to: {self.current_proxy}")
                
        return {'http': self.current_proxy, 'https': self.current_proxy}

# Пример использования
proxy_list = [
    'http://user:pass@proxy1.example.com:8000',
    'http://user:pass@proxy2.example.com:8000',
    'http://user:pass@proxy3.example.com:8000',
]

rotator = ProxyRotator(proxy_list, rotation_type='request', rotation_interval=5)

for url in urls:
    proxies = rotator.get_proxy()
    response = requests.get(url, proxies=proxies, timeout=10)

Выбор типа прокси

Тип прокси Уровень доверия Скорость Применение
Дата-центры Низкий Высокая Простой парсинг, API
Резидентные Высокий Средняя Соцсети, защищённые сайты
Мобильные Очень высокий Средняя Instagram, TikTok, антифрод

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

Browser fingerprinting и TLS-отпечатки

Даже с ротацией IP вас могут вычислить по техническим отпечаткам браузера и TLS-соединения. Эти параметры уникальны для каждого клиента и сложны в подделке.

TLS Fingerprinting

При установке HTTPS-соединения клиент отправляет ClientHello с набором поддерживаемых шифров и расширений. Эта комбинация уникальна для каждой библиотеки. Например, Python requests использует OpenSSL, отпечаток которого легко отличить от Chrome.

Проблема: Стандартные библиотеки (requests, urllib, curl) имеют отпечатки, отличные от реальных браузеров. Сервисы вроде Cloudflare, Akamai, DataDome активно используют TLS fingerprinting для блокировки ботов.

Решение: Используйте библиотеки, имитирующие TLS-отпечатки браузеров. Для Python это curl_cffi, tls_client или playwright/puppeteer для полноценной эмуляции браузера.

# Установка: pip install curl-cffi
from curl_cffi import requests

# Имитация Chrome 110
response = requests.get(
    'https://example.com',
    impersonate="chrome110",
    proxies={'https': 'http://proxy:port'}
)

# Альтернатива: tls_client
import tls_client

session = tls_client.Session(
    client_identifier="chrome_108",
    random_tls_extension_order=True
)

response = session.get('https://example.com')

HTTP/2 Fingerprinting

Помимо TLS, антибот-системы анализируют параметры HTTP/2: порядок заголовков, настройки SETTINGS фрейма, приоритеты потоков. Стандартные библиотеки не соблюдают точный порядок заголовков Chrome или Firefox.

# Правильный порядок заголовков для Chrome
headers = {
    ':method': 'GET',
    ':authority': 'example.com',
    ':scheme': 'https',
    ':path': '/',
    'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120"',
    'sec-ch-ua-mobile': '?0',
    'sec-ch-ua-platform': '"Windows"',
    'upgrade-insecure-requests': '1',
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)...',
    'accept': 'text/html,application/xhtml+xml...',
    'sec-fetch-site': 'none',
    'sec-fetch-mode': 'navigate',
    'sec-fetch-user': '?1',
    'sec-fetch-dest': 'document',
    'accept-encoding': 'gzip, deflate, br',
    'accept-language': 'en-US,en;q=0.9',
}

Canvas и WebGL fingerprinting

Браузеры отрисовывают графику по-разному в зависимости от GPU, драйверов и ОС. Сайты используют это для создания уникального отпечатка устройства. При использовании headless-браузеров (Selenium, Puppeteer) важно маскировать признаки автоматизации:

// Puppeteer: скрытие headless-режима
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');

puppeteer.use(StealthPlugin());

const browser = await puppeteer.launch({
    headless: true,
    args: [
        '--disable-blink-features=AutomationControlled',
        '--no-sandbox',
        '--disable-setuid-sandbox',
        `--proxy-server=${proxyUrl}`
    ]
});

const page = await browser.newPage();

// Переопределение navigator.webdriver
await page.evaluateOnNewDocument(() => {
    Object.defineProperty(navigator, 'webdriver', {
        get: () => false,
    });
});

Заголовки, cookies и управление сессиями

Корректная работа с HTTP-заголовками и cookies критична для имитации реального пользователя. Ошибки в этих параметрах — частая причина блокировок.

Обязательные заголовки

Минимальный набор заголовков для имитации браузера Chrome:

import requests

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
    'Accept-Language': 'en-US,en;q=0.9',
    'Accept-Encoding': 'gzip, deflate, br',
    'DNT': '1',
    'Connection': 'keep-alive',
    'Upgrade-Insecure-Requests': '1',
    'Sec-Fetch-Dest': 'document',
    'Sec-Fetch-Mode': 'navigate',
    'Sec-Fetch-Site': 'none',
    'Sec-Fetch-User': '?1',
    'Cache-Control': 'max-age=0',
}

session = requests.Session()
session.headers.update(headers)

Управление cookies

Многие сайты устанавливают tracking cookies при первом визите и проверяют их наличие при последующих запросах. Отсутствие cookies или их несоответствие — признак бота.

import requests
import pickle

class SessionManager:
    def __init__(self, session_file='session.pkl'):
        self.session_file = session_file
        self.session = requests.Session()
        self.load_session()
        
    def load_session(self):
        """Загрузка сохранённой сессии"""
        try:
            with open(self.session_file, 'rb') as f:
                cookies = pickle.load(f)
                self.session.cookies.update(cookies)
        except FileNotFoundError:
            pass
            
    def save_session(self):
        """Сохранение cookies для повторного использования"""
        with open(self.session_file, 'wb') as f:
            pickle.dump(self.session.cookies, f)
            
    def request(self, url, **kwargs):
        response = self.session.get(url, **kwargs)
        self.save_session()
        return response

# Использование
manager = SessionManager('instagram_session.pkl')
response = manager.request('https://www.instagram.com/explore/')

Важно: При ротации прокси не забывайте сбрасывать cookies, если они привязаны к конкретному IP. Несоответствие IP и cookies (например, cookies с геолокацией США и IP из Германии) вызовет подозрения.

Referer и Origin

Заголовки Referer и Origin показывают, откуда пришёл пользователь. Их отсутствие или некорректные значения — красный флаг.

# Правильная последовательность: главная → категория → товар
session = requests.Session()

# Шаг 1: заход на главную
response = session.get('https://example.com/')

# Шаг 2: переход в категорию
response = session.get(
    'https://example.com/category/electronics',
    headers={'Referer': 'https://example.com/'}
)

# Шаг 3: просмотр товара
response = session.get(
    'https://example.com/product/12345',
    headers={'Referer': 'https://example.com/category/electronics'}
)

Имитация человеческого поведения

Технические параметры — это только половина дела. Современные антибот-системы анализируют поведенческие паттерны: как пользователь взаимодействует со страницей, сколько времени проводит, как двигается мышь.

Скроллинг и движение мыши

При использовании Selenium или Puppeteer добавляйте случайные движения мыши и прокрутку страницы:

from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
import random
import time

def human_like_mouse_move(driver):
    """Случайное движение мыши по странице"""
    action = ActionChains(driver)
    
    for _ in range(random.randint(3, 7)):
        x = random.randint(0, 1000)
        y = random.randint(0, 800)
        action.move_by_offset(x, y)
        action.pause(random.uniform(0.1, 0.3))
    
    action.perform()

def human_like_scroll(driver):
    """Имитация естественного скроллинга"""
    total_height = driver.execute_script("return document.body.scrollHeight")
    current_position = 0
    
    while current_position < total_height:
        # Случайный шаг прокрутки
        scroll_step = random.randint(100, 400)
        current_position += scroll_step
        
        driver.execute_script(f"window.scrollTo(0, {current_position});")
        
        # Пауза с вариацией
        time.sleep(random.uniform(0.5, 1.5))
        
        # Иногда прокручиваем немного назад (как делают люди)
        if random.random() < 0.2:
            back_scroll = random.randint(50, 150)
            current_position -= back_scroll
            driver.execute_script(f"window.scrollTo(0, {current_position});")
            time.sleep(random.uniform(0.3, 0.8))

# Использование
driver = webdriver.Chrome()
driver.get('https://example.com')

human_like_mouse_move(driver)
time.sleep(random.uniform(2, 4))
human_like_scroll(driver)

Время на странице

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

def realistic_page_view(driver, url, min_time=5, max_time=15):
    """
    Реалистичный просмотр страницы с активностью
    """
    driver.get(url)
    
    # Начальная задержка (загрузка и "чтение")
    time.sleep(random.uniform(2, 4))
    
    # Скроллинг
    human_like_scroll(driver)
    
    # Дополнительная активность
    total_time = random.uniform(min_time, max_time)
    elapsed = 0
    
    while elapsed < total_time:
        action_choice = random.choice(['scroll', 'mouse_move', 'pause'])
        
        if action_choice == 'scroll':
            # Небольшой скролл вверх/вниз
            scroll_amount = random.randint(-200, 300)
            driver.execute_script(f"window.scrollBy(0, {scroll_amount});")
            pause = random.uniform(1, 3)
            
        elif action_choice == 'mouse_move':
            human_like_mouse_move(driver)
            pause = random.uniform(0.5, 2)
            
        else:  # pause
            pause = random.uniform(2, 5)
        
        time.sleep(pause)
        elapsed += pause

Паттерны навигации

Избегайте подозрительных паттернов: прямые переходы на глубокие страницы, игнорирование главной страницы, последовательный обход всех элементов без пропусков.

Хорошие практики:

  • Начинайте с главной страницы или популярных разделов
  • Используйте внутреннюю навигацию сайта, а не прямые URL
  • Иногда возвращайтесь назад или переходите в другие разделы
  • Варьируйте глубину просмотра: не всегда доходите до конца
  • Добавляйте "ошибки": переходы по несуществующим ссылкам, возвраты

Обход Cloudflare, DataDome и других защит

Специализированные антибот-системы требуют комплексного подхода. Они используют JavaScript-челленджи, CAPTCHA, анализ поведения в реальном времени.

Cloudflare

Cloudflare использует несколько уровней защиты: Browser Integrity Check, JavaScript Challenge, CAPTCHA. Для обхода базовой защиты достаточно корректного TLS-отпечатка и выполнения JavaScript:

# Вариант 1: cloudscraper (автоматическое решение JS-челленджей)
import cloudscraper

scraper = cloudscraper.create_scraper(
    browser={
        'browser': 'chrome',
        'platform': 'windows',
        'desktop': True
    }
)

response = scraper.get('https://protected-site.com')

# Вариант 2: undetected-chromedriver (для сложных случаев)
import undetected_chromedriver as uc

options = uc.ChromeOptions()
options.add_argument('--proxy-server=http://proxy:port')

driver = uc.Chrome(options=options)
driver.get('https://protected-site.com')

# Ожидание прохождения челленджа
time.sleep(5)

# Получение cookies для requests
cookies = driver.get_cookies()
session = requests.Session()
for cookie in cookies:
    session.cookies.set(cookie['name'], cookie['value'])

DataDome

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

from playwright.sync_api import sync_playwright
import random

def bypass_datadome(url, proxy=None):
    with sync_playwright() as p:
        browser = p.chromium.launch(
            headless=False,  # DataDome детектит headless
            proxy={'server': proxy} if proxy else None
        )
        
        context = browser.new_context(
            viewport={'width': 1920, 'height': 1080},
            user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64)...'
        )
        
        page = context.new_page()
        
        # Инъекция скриптов для маскировки автоматизации
        page.add_init_script("""
            Object.defineProperty(navigator, 'webdriver', {get: () => false});
            window.chrome = {runtime: {}};
        """)
        
        page.goto(url)
        
        # Имитация человеческого поведения
        time.sleep(random.uniform(2, 4))
        
        # Случайные движения мыши
        for _ in range(random.randint(5, 10)):
            page.mouse.move(
                random.randint(100, 1800),
                random.randint(100, 1000)
            )
            time.sleep(random.uniform(0.1, 0.3))
        
        # Скроллинг
        page.evaluate(f"window.scrollTo(0, {random.randint(300, 800)})")
        time.sleep(random.uniform(1, 2))
        
        content = page.content()
        browser.close()
        
        return content

CAPTCHA

Для автоматического решения CAPTCHA используйте сервисы распознавания (2captcha, Anti-Captcha) или стратегии избегания:

  • Снижайте частоту запросов до уровня, не вызывающего CAPTCHA
  • Используйте чистые резидентные IP с хорошей репутацией
  • Работайте через авторизованные аккаунты (у них порог CAPTCHA выше)
  • Распределяйте нагрузку по времени (избегайте пиковых часов)

Мониторинг и обработка блокировок

Даже с лучшими практиками блокировки неизбежны. Важно быстро их обнаруживать и корректно обрабатывать.

Индикаторы блокировки

Сигнал Описание Действие
HTTP 429 Too Many Requests Увеличить задержки, сменить IP
HTTP 403 Forbidden (бан IP) Сменить прокси, проверить fingerprint
CAPTCHA Требуется верификация Решить или снизить активность
Пустой ответ Контент не загружается Проверить JavaScript, cookies
Редирект на /blocked Явная блокировка Полная смена стратегии

Система повторных попыток

import requests
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry

def create_session_with_retries():
    """
    Сессия с автоматическими повторами и обработкой ошибок
    """
    session = requests.Session()
    
    retry_strategy = Retry(
        total=5,
        backoff_factor=2,  # 2, 4, 8, 16, 32 секунды
        status_forcelist=[429, 500, 502, 503, 504],
        method_whitelist=["GET", "POST"]
    )
    
    adapter = HTTPAdapter(max_retries=retry_strategy)
    session.mount("http://", adapter)
    session.mount("https://", adapter)
    
    return session

def safe_request(url, session, max_attempts=3):
    """
    Запрос с обработкой блокировок
    """
    for attempt in range(max_attempts):
        try:
            response = session.get(url, timeout=15)
            
            # Проверка на блокировку
            if response.status_code == 403:
                print(f"IP blocked. Rotating proxy...")
                # Логика смены прокси
                continue
                
            elif response.status_code == 429:
                wait_time = int(response.headers.get('Retry-After', 60))
                print(f"Rate limited. Waiting {wait_time}s...")
                time.sleep(wait_time)
                continue
                
            elif 'captcha' in response.text.lower():
                print("CAPTCHA detected")
                # Логика решения CAPTCHA или пропуска
                return None
                
            return response
            
        except requests.exceptions.Timeout:
            print(f"Timeout on attempt {attempt + 1}")
            time.sleep(5 * (attempt + 1))
            
        except requests.exceptions.ProxyError:
            print("Proxy error. Rotating...")
            # Смена прокси
            continue
            
    return None

Логирование и аналитика

Отслеживайте метрики для оптимизации стратегии:

import logging
from collections import defaultdict
from datetime import datetime

class ScraperMetrics:
    def __init__(self):
        self.stats = {
            'total_requests': 0,
            'successful': 0,
            'rate_limited': 0,
            'blocked': 0,
            'captcha': 0,
            'errors': 0,
            'proxy_failures': defaultdict(int)
        }
        
    def log_request(self, status, proxy=None):
        self.stats['total_requests'] += 1
        
        if status == 200:
            self.stats['successful'] += 1
        elif status == 429:
            self.stats['rate_limited'] += 1
        elif status == 403:
            self.stats['blocked'] += 1
            if proxy:
                self.stats['proxy_failures'][proxy] += 1
                
    def get_success_rate(self):
        if self.stats['total_requests'] == 0:
            return 0
        return (self.stats['successful'] / self.stats['total_requests']) * 100
        
    def print_report(self):
        print(f"\n=== Scraping Report ===")
        print(f"Total requests: {self.stats['total_requests']}")
        print(f"Success rate: {self.get_success_rate():.2f}%")
        print(f"Rate limited: {self.stats['rate_limited']}")
        print(f"Blocked: {self.stats['blocked']}")
        print(f"CAPTCHA: {self.stats['captcha']}")
        
        if self.stats['proxy_failures']:
            print(f"\nProblematic proxies:")
            for proxy, count in sorted(
                self.stats['proxy_failures'].items(), 
                key=lambda x: x[1], 
                reverse=True
            )[:5]:
                print(f"  {proxy}: {count} failures")

# Использование
metrics = ScraperMetrics()

for url in urls:
    response = safe_request(url, session)
    if response:
        metrics.log_request(response.status_code, current_proxy)
    
metrics.print_report()

Оптимальные показатели: Success rate выше 95% — отличный результат. 80-95% — приемлемо, но есть что улучшать. Ниже 80% — пересмотрите стратегию: возможно, слишком агрессивный rate limiting, плохие прокси или проблемы с fingerprinting.

Заключение

Защ