Веб-скрейпинг и LLM

Python веб-скрейпинг и извлечение данных для LLM (2026)

Быстрый ответ: В 2026 году для эффективных python web scraping projects под задачи LLM требуется двухэтапный конвейер: асинхронный headless-рендеринг (Playwright или Crawl4AI) с алгоритмической очисткой DOM-шума (удаление SVG, скриптов, меню) перед передачей в модель. Для структурированных данных применяйте Pydantic-схемы через Instructor или нативный JSON mode, что снижает затраты на инференс на 75–88%.


1. Введение: Веб-скрейпинг в эпоху фронтирных LLM и автономных ИИ-агентов

Автономные ИИ-агенты, пайплайны генерации с дополненной выборкой (RAG) и аналитические системы критически зависят от свежего и достоверного контекста. Хотя контекстные окна современных моделей (таких как Claude 3.7 Sonnet, DeepSeek V3/R1 и GPT-4o) достигают от 128k до 2 млн токенов, прямая подача сырого HTML в трансформерные механизмы внимания — один из самых дорогостоящих антипаттернов в современной инженерии.

Исторически, когда инженеру требовалось решить задачу scrape website python, подход был прямолинейным: скачать HTML через requests или urllib, распарсить дерево DOM библиотеками BeautifulSoup или lxml, либо запустить распределенного паука на Scrapy. Для статических страниц эти инструменты остаются быстрыми, однако современный веб превратился в динамические Single Page Applications (SPA) на Next.js, React и Vue, защищенные периметрами WAF (Cloudflare Turnstile, DataDome, Akamai, AWS WAF).

Параллельно изменилась сама цель сбора данных:

  • Классический скрейпинг (2015–2023): Извлечение конкретных полей в строки реляционной БД (цена, артикул, дата) по жестким CSS/XPath-селекторам.
  • Агентный скрейпинг для LLM (2026): Сбор полуструктурированных страниц, фильтрация навигационного шума, сохранение семантической структуры (заголовки, markdown-таблицы, фрагменты кода) и валидация строгого JSON по схемам Pydantic.
Архитектура конвейера извлечения веб-данных для LLM (2026):
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                                    Целевой веб-ресурс                                   │
│            (Next.js/React SPA, динамическая гидратация, защита Cloudflare WAF)          │
└───────────────────────────────────────────┬─────────────────────────────────────────────┘
                                            │ Пул резидентных прокси (Bright Data / Oxylabs)
                                            ▼ Маскировка TLS JA4 и отпечатков сессий
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                        Уровень 1: Высокопроизводительный сбор                           │
│   ┌───────────────────────────┐ ┌───────────────────────────┐ ┌─────────────────────┐   │
│   │ Статический HTML          │ │ Async Headless Браузер    │ │ Движок Crawl4AI     │   │
│   │ curl_cffi / httpx (HTTP/2)│ │ Playwright Async Engine   │ │ PruningContentFilter│   │
│   └───────────────────────────┘ └───────────────────────────┘ └─────────────────────┘   │
└───────────────────────────────────────────┬─────────────────────────────────────────────┘
                                            │ Сырой HTML / Отрендеренное дерево DOM
                                            ▼
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                     Уровень 2: Очистка шума DOM и оптимизация токенов                   │
│  - Удаление тегов <script>, <style>, <nav>, <footer>, <svg>, трекеров и CSS-стилей       │
│  - Алгоритмическая фильтрация: метрика плотности текста к тегам и BM25-ранжирование      │
│  - Конвертация в семантический Markdown с сохранением структуры таблиц                  │
│  - Результат: сокращение расхода токенов на 78% – 88% до передачи в промпт LLM          │
└───────────────────────────────────────────┬─────────────────────────────────────────────┘
                                            │ Очищенный Markdown / Текстовые чанки
                                            ▼
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                 Уровень 3: Структурированное извлечение и валидация Pydantic            │
│  - Определение моделей Pydantic BaseModel с ограничениями полей и regex-проверками      │
│  - Instructor / OpenAI Structured Outputs / DeepSeek V3 JSON mode с автоповторами       │
│  - Гарантированное отсутствие галлюцинаций в схемах для сохранения в Postgres / Vector  │
└─────────────────────────────────────────────────────────────────────────────────────────┘

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

  1. Выбрать оптимальный фреймворк (BeautifulSoup vs Scrapy vs Playwright vs Crawl4AI).
  2. Настроить алгоритмическую очистку DOM-дерева для минимизации токенов.
  3. Обеспечить строгую валидацию схем через Pydantic.
  4. Организовать надежную ротацию резидентных прокси и маскировку TLS-отпечатков.

2. Сравнение фреймворков: BeautifulSoup vs Scrapy vs Playwright vs Crawl4AI

Каждый инструмент оптимизирован под свой класс задач. Выбор зависит от потребности в рендеринге JavaScript, масштабов парсинга (страниц в секунду) и нативной поддержки LLM.

Сводная архитектурная матрица

Критерий оценки BeautifulSoup4 + Requests Scrapy (Twisted/AsyncIO) Raw Playwright (Async Python) Crawl4AI (v0.9.x+)
Основной сценарий Небольшие скрипты и статический HTML Масштабный распределенный сбор Динамические SPA и сложный JS Агенты RAG и извлечение под LLM
Исполнение JavaScript Отсутствует Нет (требуется Scrapy-Playwright) Полный движок Chromium, WebKit Оптимизированный Chromium
Пропускная способность ~15–25 запр/с на воркер ~150–300 запр/с на паука ~5–12 стр/с на 16 ГБ RAM ~25–45 стр/с (пул контекстов)
Потребление памяти Минимальное (~40 МБ/воркер) Низкое (~120 МБ/паук) Высокое (150–350 МБ/контекст) Среднее (~80–140 МБ/вкладку)
Конвертация в Markdown Сторонние (html2text/markdownify) Сторонние пайплайны Ручная интеграция Встроенный LLM-Markdown
Очистка шума DOM Ручное удаление тегов Ручные селекторы XPath/CSS Вызовы CDP evaluate в DOM Встроенный PruningContentFilter
Обход антибот-систем Базовый (только User-Agent) Прокси-мидлвары и ротация Продвинутый (playwright-stealth) Встроенный стелс и эмуляция TLS
Поддержка Pydantic Нет (ручной парсинг) ItemLoaders и Scrapy Items Нет (ручной парсинг) Нативный движок Pydantic/LLM
Порог входа Очень низкий Высокий (архитектура Twisted) Средний Низкий / Средний

1. BeautifulSoup4 (bs4): Легковесный парсер статики

BeautifulSoup остается классическим решением для быстрого прототипирования. В связке с httpx или curl_cffi (для маскировки TLS) библиотека гарантирует мгновенный разбор статического HTML без нагрузки на процессор.

  • Плюсы: Нулевой оверхед браузера, устойчивость к невалидной верстке благодаря парсеру lxml, простота синтаксиса.
  • Минусы: Полная слепота к клиентской гидратации (React/Next.js возвращают пустой
    ), отсутствие встроенной асинхронности, необходимость вручную писать преобразование в Markdown.

2. Scrapy: Асинхронный комбайн для больших объемов

Архитектура на базе Twisted и нативного asyncio делает Scrapy непревзойденным лидером для сканирования сотен тысяч статических страниц. Встроенные Item Pipelines, пауки и связка с Redis (scrapy-redis) формируют стандарт корпоративного сбора данных.

  • Плюсы: Огромная скорость (сотни страниц в секунду), автоматический разбор robots.txt, очередь обхода ссылок.
  • Минусы: Избыточная сложность для задач AI-агентов; рендеринг JS через scrapy-playwright нивелирует экономию памяти; отсутствие встроенной подготовки разметки под LLM.

3. Playwright для Python: Промышленный стандарт автоматизации

Поддерживаемый Microsoft пакет playwright-python предоставляет детальный асинхронный контроль над Chromium, Firefox и WebKit через протокол Chrome DevTools (CDP).

  • Плюсы: Идеальная работа со сложными SPA, бесконечным скроллингом, эмуляцией куки и перехватом сетевых пакетов.
  • Минусы: Высокое потребление RAM и CPU; стандартный Playwright легко вычисляется WAF по флагам автоматизации (navigator.webdriver); на выходе получается тяжелый сырой HTML.

4. Crawl4AI: Специализированный фреймворк для LLM

Созданный специально для задач искусственного интеллекта и RAG, Crawl4AI объединяет возможности Playwright с мощным пайплайном фильтрации DOM и генерации Markdown.

  • Плюсы: Снижение объема токенов до 88% «из коробки»; встроенный фильтр PruningContentFilter; автоматический обход блокировок; нативное извлечение данных в Pydantic через локальные модели (Ollama) или облачные API; эффективный пул вкладок.
  • Минусы: Ориентирован на сбор контента страниц, а не на построение графов обхода миллионов URL.

3. Экономика сбора данных для LLM: очистка DOM-шума

Передача сырого HTML в промпты LLM приводит к финансовой катастрофе. Типичная посадочная страница или документация содержит:

  • Размер сырого HTML: от 850 КБ до 2,4 МБ.
  • Объем в токенах: от 45 000 до 120 000 токенов.
  • Полезное семантическое ядро (текст, таблицы, код): всего 1 200 – 3 500 токенов.

В результате до 95% бюджета на инференс сжигается на классы стилей (class="flex items-center justify-between p-4 dark:bg-slate-900..."), base64-изображения, координаты SVG, скрипты аналитики и ссылки футера.

Расчет экономии на объеме 50 000 страниц в день

Сравним расходы на обработку 50 000 страниц при подаче в Claude 3.7 Sonnet ($3.00/1M токенов) или GPT-4o ($2.50/1M токенов):

$$\text{Сырые токены в день} = 50{,}000 \times 55{,}000 = 2{,}750{,}000{,}000 \text{ токенов (2,75 млрд)}$$ $$\text{Токены после очистки в Markdown} = 50{,}000 \times 4{,}200 = 210{,}000{,}000 \text{ токенов (210 млн)}$$

$$\text{Экономия в день (при \$2.50 / 1M токенов)} = (2{,}750 - 210) \times \$2.50 = \$6{,}350.00 \text{ / день}$$ $$\mathbf{Экономия\;в\;месяц = \$190,500.00 / месяц}$$

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


4. Практическая реализация: Async Playwright и фильтрация Selectolax

Ниже представлен асинхронный скрипт на Python с использованием playwright-python и ультрабыстрого C-парсера selectolax, удаляющего шум на уровне оперативной памяти:

# clean_scraper.py
import asyncio
from typing import Optional
from playwright.async_api import async_playwright, Browser, Page
from selectolax.parser import HTMLParser
import markdownify

class LLMWebScraper:
    def __init__(self, headless: bool = True):
        self.headless = headless
        self.browser: Optional[Browser] = None

    async def initialize(self):
        playwright = await async_playwright().start()
        # Запуск Chromium с флагами маскировки
        self.browser = await playwright.chromium.launch(
            headless=self.headless,
            args=[
                "--disable-blink-features=AutomationControlled",
                "--no-sandbox",
                "--disable-setuid-sandbox",
                "--disable-dev-shm-usage",
            ]
        )

    async def scrape_clean_markdown(self, url: str) -> str:
        if not self.browser:
            await self.initialize()

        context = await self.browser.new_context(
            user_agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36",
            viewport={"width": 1920, "height": 1080},
        )
        page: Page = await context.new_page()

        try:
            await page.goto(url, wait_until="networkidle", timeout=30000)
            await page.evaluate("window.scrollBy(0, document.body.scrollHeight / 2);")
            await asyncio.sleep(1.0)
            raw_html = await page.content()
        finally:
            await context.close()

        # Очистка дерева DOM через Selectolax
        cleaned_html = self.prune_dom_tree(raw_html)

        # Конвертация в семантический Markdown
        clean_markdown = markdownify.markdownify(
            cleaned_html,
            heading_style="ATX",
            strip=['img', 'a', 'form'],
            bullets="-"
        )
        
        return "\n".join([line for line in clean_markdown.splitlines() if line.strip()])

    @staticmethod
    def prune_dom_tree(html: str) -> str:
        # Удаление скриптов, стилей, SVG и навигационных блоков
        parser = HTMLParser(html)

        tags_to_decompose = [
            "script", "style", "svg", "noscript", "iframe",
            "header", "footer", "nav", "aside", "form"
        ]
        for tag in tags_to_decompose:
            for node in parser.css(tag):
                node.decompose()

        boilerplate_selectors = [
            ".advertisement", ".ad-container", "#cookie-banner", 
            ".cookie-consent", ".newsletter-popup", ".social-share",
            "[role='alert']", "[aria-hidden='true']"
        ]
        for selector in boilerplate_selectors:
            for node in parser.css(selector):
                node.decompose()

        main_content = parser.css_first("main, article, #content, .content, .post-body")
        if main_content:
            return main_content.html

        body = parser.css_first("body")
        return body.html if body else parser.html

async def main():
    scraper = LLMWebScraper(headless=True)
    await scraper.initialize()
    url = "https://news.ycombinator.com"
    markdown = await scraper.scrape_clean_markdown(url)
    print(f"Извлеченный Markdown (первые 500 символов):\n{markdown[:500]}")
    if scraper.browser:
        await scraper.browser.close()

if __name__ == "__main__":
    asyncio.run(main())

5. Пайплайн нового поколения на Crawl4AI

Фреймворк Crawl4AI предоставляет готовый асинхронный стек, оптимизированный под работу с языковыми моделями.

Ключевые возможности Crawl4AI

  1. PruningContentFilter: Анализирует отношение текста к тегам и глубину вложенности DOM для отсечения второстепенных блоков без ручного подбора селекторов.
  2. BM25ContentFilter: Ранжирует текстовые фрагменты по релевантности запросу пользователя, отбирая только необходимые чанки.
  3. Пул вкладок: Эффективное повторное использование инстанса браузера сокращает расход RAM более чем на 60%.

Пример использования Crawl4AI в продакшене

# crawl4ai_pipeline.py
import asyncio
from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig, CacheMode
from crawl4ai.content_filter_strategy import PruningContentFilter
from crawl4ai.markdown_generation_strategy import DefaultMarkdownGenerator

async def run_crawl4ai_extraction(target_url: str) -> str:
    browser_config = BrowserConfig(
        headless=True,
        verbose=False,
        extra_args=["--disable-gpu", "--disable-dev-shm-usage", "--no-sandbox"]
    )

    content_filter = PruningContentFilter(
        threshold=0.48,
        threshold_type="fixed",
        min_word_threshold=10
    )

    markdown_generator = DefaultMarkdownGenerator(
        content_filter=content_filter,
        options={"ignore_links": False, "ignore_images": True}
    )

    crawl_config = CrawlerRunConfig(
        cache_mode=CacheMode.BYPASS,
        markdown_generator=markdown_generator,
        word_count_threshold=20,
        page_timeout=30000,
        wait_until="networkidle"
    )

    async with AsyncWebCrawler(config=browser_config) as crawler:
        result = await crawler.arun(url=target_url, config=crawl_config)

        if not result.success:
            raise RuntimeError(f"Ошибка сбора: {result.error_message}")

        print(f"[Метрики] Токены сырого HTML: ~{len(result.html)//4}")
        print(f"[Метрики] Токены чистого Markdown: ~{len(result.markdown)//4}")
        return result.markdown

if __name__ == "__main__":
    url = "https://en.wikipedia.org/wiki/Large_language_model"
    cleaned_md = asyncio.run(run_crawl4ai_extraction(url))
    print(f"\nОчищенный Markdown:\n{cleaned_md[:600]}")

6. Структурированное извлечение JSON через Pydantic и Instructor

Текстовый Markdown подходит для векторных хранилищ, но автономным агентам необходим валидный типизированный JSON. Использование Pydantic в связке с Instructor гарантирует получение структурированных объектов с автоматическим исправлением ошибок валидации:

# pydantic_extractor.py
import os
from typing import List, Optional
from pydantic import BaseModel, Field
import instructor
from openai import OpenAI

class FeatureSpecification(BaseModel):
    name: str = Field(description="Название технической функции или бенчмарка")
    value: str = Field(description="Значение параметра")
    supported: bool = Field(description="Поддерживается ли нативно")

class ProductComparison(BaseModel):
    product_name: str = Field(description="Название продукта или модели")
    pricing_model: str = Field(description="Модель оплаты (Free, Pay-as-you-go, Subscription)")
    monthly_price_usd: Optional[float] = Field(default=None, description="Базовая цена в USD")
    features: List[FeatureSpecification] = Field(description="Список параметров")
    summary: str = Field(description="Краткое резюме из 2 предложений")

def extract_structured_data(clean_markdown: str) -> ProductComparison:
    client = instructor.from_openai(OpenAI(api_key=os.environ.get("OPENAI_API_KEY", "your-key")))

    extracted_data: ProductComparison = client.chat.completions.create(
        model="gpt-4o-mini",
        response_model=ProductComparison,
        max_retries=3,
        messages=[
            {
                "role": "system",
                "content": "Вы — движок извлечения данных. Извлекайте сущности строго по контексту."
            },
            {
                "role": "user",
                "content": f"Контекст:\n\n{clean_markdown}"
            }
        ],
        temperature=0.0,
    )
    return extracted_data

7. Обход защит WAF и ротация прокси-серверов

Современные WAF вычисляют парсеры по четырем признакам:

  1. Репутация IP: Диапазоны датацентров блокируются сразу; в продакшене обязательны резидентные прокси.
  2. Отпечаток TLS ClientHello (JA3/JA4): Стандартный Python requests выдает сигнатуру OpenSSL.
  3. Фреймы HTTP/2: Проверка порядка заголовков и размеров окон.
  4. Утечки в Headless-браузере: Флаги автоматизации (navigator.webdriver).

Использование curl_cffi для быстрого стелс-сбора

Если странице не нужен JavaScript, библиотека curl_cffi позволяет эмулировать отпечатки Chrome без тяжелого браузера:

# stealth_requester.py
from curl_cffi import requests

def fetch_stealth_html(url: str, proxy_url: str = None) -> str:
    # Запрос с маскировкой отпечатка TLS под Chrome
    proxies = {"http": proxy_url, "https": proxy_url} if proxy_url else None

    response = requests.get(
        url,
        impersonate="chrome124",
        proxies=proxies,
        timeout=15,
        headers={
            "Accept-Language": "en-US,en;q=0.9",
            "Sec-Ch-Ua": '"Not A(Brand";v="99", "Google Chrome";v="124", "Chromium";v="124"',
            "Sec-Ch-Ua-Mobile": "?0",
            "Sec-Ch-Ua-Platform": '"macOS"',
        }
    )
    if response.status_code != 200:
        raise ConnectionError(f"Блокировка: статус {response.status_code}")
    return response.text

8. Итоговая экономика и себестоимость обработки 100 000 страниц

Статья расходов Raw Playwright (сырой HTML) Async Playwright + Selectolax Движок Crawl4AI Firecrawl Cloud
Вычислительные мощности (AWS) $48.00 $22.00 $16.00 $0.00 (SaaS)
Трафик резидентных прокси $120.00 (150 ГБ) $32.00 (Блокировка ассетов) $30.00 (Фильтрация) Включено в SaaS
Инференс LLM (GPT-4o) $13,750.00 (5,5 млрд токенов) $1,050.00 (420 млн токенов) $975.00 (390 млн токенов) $825.00
Лицензии и подписки $0.00 $0.00 $0.00 $199.00
Итоговая стоимость пайплайна $13,918.00 $1,104.00 $1,021.00 $1,024.00

Инженерия сокращения токенов окупается многократно: предварительная очистка экономит более $12 800 на каждые 100 000 обработанных страниц.


9. Заключение и архитектурные рекомендации

  1. Многоуровневая стратегия: Сначала пробуйте быстрый curl_cffi, а Playwright или Crawl4AI запускайте только при наличии динамической клиентской гидратации.
  2. Обязательная фильтрация DOM: Никогда не передавайте сырой HTML в LLM. Стремитесь к сжатию токенов более 80%.
  3. Строгая валидация Pydantic: Используйте instructor для автоматического исправления ошибок в JSON-схемах.
  4. Разделение задач: Скрейпинг должен работать в асинхронной очереди (Celery/ARQ), сохраняя очищенный Markdown в кэш перед вызовом нейросети.
← Все статьи
0 / 4
Сравнить →