Все статьиРуководства7 мин

Скрейпинг новостных агрегаторов на Python для маркетинговых исследований

Продакшн-ready пайплайн на Python: extract_rules, TLS-профили, эмуляция поведения, networkidle vs load, session_id и масштабирование через async и batch.

Команда InfraProxy

20 февраля 2026 г.

#новостные агрегаторы#Python#маркетинговые исследования#extract_rules#API для скрейпинга

Почему новостные агрегаторы сложно скрейпить в 2026 году

Новостные агрегаторы — ценный источник для маркетинговых исследований, мониторинга репутации и анализа медиа-покрытия. Но в 2026 году извлечение данных с них стало значительно сложнее из-за усиления антибот-защит и динамической загрузки контента.

Rate limiting и CAPTCHA. Агрегаторы ограничивают количество запросов с одного IP. После 20–50 запросов — блокировка или CAPTCHA. Массовый сбор с одного датацентрового IP не работает.

TLS-отпечатки. Python-клиенты (requests, httpx) используют OpenSSL, чей отпечаток отличается от реального Chrome. Системы защиты детектируют это и блокируют запросы.

JavaScript и динамическая подгрузка. Список новостей часто рендерится на клиенте: при скролле подгружаются новые карточки, контент приходит через XHR. Обычный HTTP-запрос возвращает пустой или неполный HTML.

Поведенческий анализ. Продвинутые системы отслеживают паттерны навигации. Запросы без задержек, без скролла, с подозрительно быстрой последовательностью — признаки бота.

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

Продакшн-ready пайплайн на Python с extract_rules

Структура запроса

import httpx
import os

API_KEY = os.environ.get("SCRAPER_API_KEY")
BASE_URL = "https://api.example.com/v1/scrape"

extract_rules = {
    "query": "h1.search-headline, .search-title",
    "items": {
        "selector": "article.news-item, .search-result-item",
        "type": "list",
        "fields": {
            "title": "h2, .title, a",
            "link": "a@href",
            "source": ".source-name, .publisher",
            "date": "time@datetime, .date",
            "snippet": ".snippet, .description"
        }
    }
}

Селекторы подбираются под структуру конкретного агрегатора. Синтаксис @href и @datetime извлекает атрибуты элементов.

Полный пример скрипта

def scrape_news_aggregator(query: str, country: str = "ru") -> list[dict]:
    url = f"https://news.example.com/search?q={query}&gl={country}"
    payload = {
        "url": url,
        "use_js_render": True,
        "use_residential": True,
        "country": country,
        "extract_rules": extract_rules
    }
    with httpx.Client(timeout=45.0) as client:
        resp = client.post(
            BASE_URL,
            json=payload,
            headers={
                "Authorization": f"Bearer {API_KEY}",
                "Content-Type": "application/json"
            }
        )
        resp.raise_for_status()
        data = resp.json()
        return data.get("extract", {}).get("items", [])

Параметр country обеспечивает геотаргетинг — выдача новостей зависит от региона. Для российского рынка используйте country: "ru".

TLS-профили и поведенческая эмуляция

API для скрейпинга по умолчанию использует TLS-отпечаток Chrome и эмулирует поведение реального пользователя:

  • Корректные заголовки (User-Agent, Accept-Language, Sec-CH-UA и др.)
  • Последовательность загрузки ресурсов, близкая к браузеру
  • Задержки между действиями при необходимости
  • Прохождение JavaScript-челленджей

Явно указывать эти параметры обычно не нужно. При низком success rate проверьте: включены ли резидентные прокси, достаточен ли таймаут, не блокируется ли запрос по гео.

Подводные камни: networkidle vs load

wait_for: "load" — ожидание события load (DOM загружен, основные ресурсы получены). Быстрее, но на страницах с тяжёлой аналитикой или long polling контент может ещё подгружаться.

wait_for: "networkidle" — ожидание прекращения сетевой активности (нет запросов дольше N мс). Страницы новостных агрегаторов часто делают постоянные фоновые запросы (метрика, обновление счётчиков). В таком случае networkidle может никогда не наступить или ждать десятки секунд.

Рекомендация: используйте явное ожидание конкретного элемента — селектор контейнера со списком новостей (.search-results, .news-list и т.п.). Это надёжнее и быстрее, чем networkidle на «шумных» страницах.

{
  "url": "https://news.example.com/search?q=...",
  "use_js_render": true,
  "js_wait_for": ".news-list, .search-results",
  "extract_rules": { ... }
}

extract_rules vs extract_prompt

extract_rules — CSS-селекторы, декларативное описание. Быстро, дёшево. При изменении вёрстки агрегатора селекторы нужно обновлять. Подходят для стабильных источников.

extract_prompt — текстовое описание того, что извлечь, плюс необязательная схема. Модель интерпретирует страницу и возвращает структурированные данные. Устойчивее к изменениям, но дороже и медленнее. Имеет смысл при парсинге множества разнородных агрегаторов или при нестабильной разметке.

Для начала используйте extract_rules; при частых поломках переходите на extract_prompt.

session_id для rate-limited задач

При сборе нескольких страниц одного агрегатора (пагинация, разные запросы) используйте один и тот же session_id. API направляет трафик через один прокси и сохраняет cookies — снижается вероятность CAPTCHA при «естественном» поведении в рамках одной сессии.

payload = {
    "url": url,
    "session_id": "news-aggregator-session-001",
    "use_js_render": True,
    "extract_rules": extract_rules
}

Не переиспользуйте один session_id бесконечно: при длительных прогонах лучше ротировать сессии (например, новая сессия каждые 50 запросов).

Масштабирование: async и batch-эндпоинты

Для мониторинга сотен запросов в день используйте асинхронный подход:

import asyncio
import httpx

async def scrape_queries(queries: list[str], max_concurrent: int = 5) -> list[dict]:
    results = []
    sem = asyncio.Semaphore(max_concurrent)

    async def fetch_one(client: httpx.AsyncClient, q: str):
        async with sem:
            url = f"https://news.example.com/search?q={q}"
            payload = {
                "url": url,
                "use_js_render": True,
                "use_residential": True,
                "extract_rules": extract_rules
            }
            resp = await client.post(BASE_URL, json=payload, headers={"Authorization": f"Bearer {API_KEY}"})
            resp.raise_for_status()
            return {"query": q, "items": resp.json().get("extract", {}).get("items", [])}

    async with httpx.AsyncClient(timeout=60.0) as client:
        tasks = [fetch_one(client, q) for q in queries]
        results = await asyncio.gather(*tasks)
    return results

При больших объёмах (десятки URL в одном прогоне) рассмотрите batch-эндпоинт: один запрос с массивом URL, единый ответ с массивом результатов. Меньше накладных расходов, проще оркестрация.

Резюме

  • Новостные агрегаторы в 2026 году защищены rate limiting, TLS-проверками и JavaScript-зависимым контентом
  • Продакшн-пайплайн: Python + API для скрейпинга + extract_rules + резидентные прокси
  • Используйте js_wait_for на контейнер с контентом вместо networkidle на «шумных» страницах
  • extract_rules — для стабильных источников; extract_prompt — при нестабильной разметке
  • session_id снижает вероятность CAPTCHA при многостраничном сборе
  • Масштабируйте через async с ограничением concurrency или batch-эндпоинт

Читайте также

InfraProxy Scraper API обеспечивает резидентные прокси и TLS-профили для стабильного сбора данных с новостных агрегаторов в рамках маркетинговых и репутационных исследований. Узнайте условия.

Нужны надёжные прокси для вашего проекта?

InfraProxy предоставляет серверные и резидентные прокси для российского бизнеса. Договор, постоплата, техподдержка.