Все статьиРуководстваПрокси-инфраструктура8 мин

Надёжный веб-скрейпер на Python: архитектура и лимиты

Как построить Python-скрейпер с httpx и Pydantic: лимиты, обработка ошибок, кэш, очереди и контролируемое масштабирование.

Команда InfraProxy

21 февраля 2026 г.

#Python#HTTP#backoff#httpx#асинхронный скрейпинг

Почему связка requests + BeautifulSoup больше не работает в 2026 году

Классический стек — requests для HTTP и BeautifulSoup для парсинга HTML — десятилетиями использовался для сбора данных. Сегодня он перестаёт быть достаточным для большинства коммерчески значимых источников.

Проблема 1: сетевые ответы. Источник может вернуть 403, 429 или 503. Эти статусы нужно сохранять отдельно от сетевых ошибок и обрабатывать ограниченным backoff.

Проблема 2: JavaScript. Большая часть современного контента рендерится в браузере. Запрос через requests возвращает пустой или неполный HTML — данные подгружаются через XHR/Fetch после выполнения скриптов.

Проблема 3: лимиты. Без задержек, кэша и ограниченной параллельности клиент создаёт лишнюю нагрузку и быстро получает 429.

Если источник требует CAPTCHA, входа или запрещает автоматический сбор, остановите задачу и используйте официальный API либо согласованный доступ.

Архитектура: Scraper API как сетевой слой

Scraper API — это облачный сервис, который принимает URL и возвращает готовый HTML (или структурированные данные). Внутри он использует:

  • Резидентные прокси с ротацией
  • JavaScript-рендеринг (Playwright/Puppeteer)
  • Ограниченные повторные попытки при временных ошибках

Вы пишете простой клиент на Python: отправляете запросы к API, получаете данные, парсите их локально. Вся логика обработка ограничений инкапсулирована в сервисе.

Преимущества такого подхода:

  • Быстрый старт без настройки инфраструктуры
  • Предсказуемая стоимость (плата за успешный запрос)
  • Единая обработка сетевых ошибок и лимитов
  • Возможность сосредоточиться на бизнес-логике (извлечение, хранение, аналитика)

Настройка Python-окружения

Для работы с Scraper API достаточно стандартных библиотек HTTP и валидации данных:

# requirements.txt
httpx>=0.27.0
pydantic>=2.0.0

httpx — современная асинхронная HTTP-библиотека с поддержкой HTTP/2, совместимая с requests по интерфейсу. pydantic — для описания схем извлекаемых данных и валидации ответов.

import httpx
from pydantic import BaseModel

class ScraperClient:
    def __init__(self, api_key: str, base_url: str = "https://api.example.com/v1"):
        self.api_key = api_key
        self.base_url = base_url

    async def scrape(self, url: str, render_js: bool = True) -> str:
        async with httpx.AsyncClient(timeout=60.0) as client:
            resp = await client.post(
                f"{self.base_url}/scrape",
                json={"url": url, "render_js": render_js},
                headers={"Authorization": f"Bearer {self.api_key}"}
            )
            resp.raise_for_status()
            return resp.json()["html"]

Базовая интеграция не требует сложной логики — достаточно корректно формировать запросы и обрабатывать ответы.

Управление сетевым маршрутом

Scraper API объединяет несколько технических операций:

  • Выбор прокси — подходящий тип адреса под разрешённую задачу
  • JavaScript-рендеринг — получение данных, которые отсутствуют в исходном HTML
  • Лимиты — ограниченная параллельность и паузы между запросами
  • Диагностика — раздельный учёт HTTP-статусов и сетевых ошибок

При необходимости можно явно указать параметры:

  • country — геотаргетинг (например, для локальных вариантов сайта)
  • proxy_type: "residential" — принудительное использование резидентных прокси

Для большинства задач достаточно стандартных настроек; тонкая подстройка нужна только при низком success rate.

CAPTCHA и закрытые данные

CAPTCHA — сигнал остановить автоматический сценарий и проверить правила источника. Не собирайте данные за логином или paywall. Для таких данных используйте официальный API или письменное разрешение владельца.

LLM-экстракция структурированных данных

Классический парсинг опирается на CSS/XPath-селекторы. При частых изменениях вёрстки селекторы ломаются, парсеры требуют доработки. Альтернатива — извлечение с помощью LLM: вы передаёте HTML (или текст) и схему полей, модель возвращает структурированный JSON.

class Product(BaseModel):
    title: str
    price: float
    currency: str
    in_stock: bool

# В запросе к API указываем extract_schema на основе Pydantic-модели
extract_schema = {
    "title": {"type": "string"},
    "price": {"type": "number"},
    "currency": {"type": "string"},
    "in_stock": {"type": "boolean"}
}

API, поддерживающий AI-экстракцию, принимает такую схему и возвращает готовый JSON. Преимущества: устойчивость к редизайнам, работа с неструктурированным текстом. Недостатки: выше стоимость и время ответа. Для стабильных источников CSS-селекторы остаются экономичным вариантом; LLM имеет смысл для разнородных или часто меняющихся сайтов.

Масштабирование до 10 000 страниц: асинхронный батчинг

При объёмах в тысячи страниц критична параллельность. Синхронные запросы в цикле работают слишком медленно. Необходим асинхронный подход с ограничением concurrency, чтобы не превышать лимиты API и не перегружать целевые сайты.

import asyncio
from httpx import AsyncClient, Limits

async def scrape_batch(urls: list[str], api_key: str, max_concurrent: int = 10) -> list[dict]:
    limits = Limits(max_connections=20, max_keepalive_connections=10)
    results = []

    async def fetch_one(client: AsyncClient, url: str) -> dict | None:
        try:
            resp = await client.post(
                "https://api.example.com/v1/scrape",
                json={"url": url, "render_js": True},
                headers={"Authorization": f"Bearer {api_key}"}
            )
            resp.raise_for_status()
            return {"url": url, "html": resp.json()["html"], "status": "ok"}
        except Exception as e:
            return {"url": url, "error": str(e), "status": "failed"}

    async with AsyncClient(timeout=60.0, limits=limits) as client:
        sem = asyncio.Semaphore(max_concurrent)
        async def limited_fetch(url):
            async with sem:
                return await fetch_one(client, url)
        tasks = [limited_fetch(url) for url in urls]
        results = await asyncio.gather(*tasks)

    return results

Ключевые моменты:

  • Semaphore — ограничение одновременных запросов (например, 10–20)
  • Connection pooling — переиспользование соединений через AsyncClient
  • Обработка ошибок — логирование неудачных URL для повторной обработки
  • Ретраи — экспоненциальный backoff при 429/503

При 10 000 URL и 10 параллельных запросах, при среднем времени ответа 5 секунд, полный прогон займёт ориентировочно 1–2 часа (с учётом ограничений и повторов).

Компромиссы и практические выводы

Плюсы Scraper API:

  • Не нужно отдельно поддерживать прокси, браузеры и очереди
  • Быстрый вывод продукта на рынок
  • Предсказуемая стоимость при переменных объёмах

Минусы:

  • Зависимость от провайдера
  • При очень больших стабильных объёмах собственное решение с прокси может быть дешевле
  • Ограничения по кастомизации (особые заголовки, сложные сценарии взаимодействия)

Гибридный подход:

  • Простые, незащищённые сайты — собственный скрейпер + прокси InfraProxy
  • Защищённые маркетплейсы, площадки с вакансиями, соцсети — Scraper API
  • Комбинирование обоих вариантов в одном пайплайне

Для старта разумно начать с API: проверить гипотезу, оценить success rate и стоимость. При росте объёмов и стабилизации требований можно перенести часть нагрузки на DIY-решение с качественными резидентными прокси.

Перед рабочим запуском проверьте задачу на разрешённом источнике: условия теста прокси.

Этичность и ограничения

Перед сбором проверьте официальный API, robots.txt и условия использования источника. Соблюдайте Disallow и Crawl-delay, ограничивайте параллельность, используйте backoff на 429/503 и кэшируйте ответы. Собирайте только публичные данные: без обхода логина и paywall, без персональных данных и перепубликации чужих текстов. Используйте честный User-Agent и обрабатывайте запросы на удаление данных.

Нужна прокси-инфраструктура под вашу нагрузку?

Сравните Datacenter и ISP прокси, протоколы и условия теста перед договором.

Перейти к прокси