Качественный RAG (Retrieval-Augmented Generation) и автономные ИИ-агенты начинаются не с модели, а с чистоты входных данных. Попытка скормить языковой модели сырую веб-страницу через обычные парсеры оборачивается двумя крайностями: классические библиотеки вроде BeautifulSoup бессильны перед динамическими SPA на React и Vue, а «в лоб» запущенный Playwright выгружает мегабайты неструктурированного HTML с шапками, баннерами, рекламными скриптами и куки-плашками. В результате львиная доля контекстного окна LLM тратится на бесполезный шум, а платные облачные API вроде Firecrawl требуют регулярной подписки в валюте и передают данные ваших клиентов на чужие серверы. Crawl4AI решает эту задачу автономно: это открытый асинхронный краулер на Python с более чем 86 000 звёзд на GitHub, упакованный в готовый Docker-образ. Он эмулирует реальный браузер без GUI, бережно фильтрует разметку и выдаёт компактный Markdown, подготовленный для эмбеддингов и векторных баз. В этом руководстве мы развернём Crawl4AI v0.9.5 на собственном VDS в Docker Compose, оптимизируем память Chromium для работы без GPU и настроим безопасный REST API с авторизацией по токену.
1. Почему обычные парсеры и Playwright не подходят для LLM и RAG
Когда разработчик начинает собирать базу знаний для локальной нейросети, первая мысль — написать скрипт на requests и BeautifulSoup. Однако значительная часть современного веб-контента рендерится на клиенте через JavaScript (React, Next.js, Angular, Vue). Обычный HTTP-клиент получает лишь пустой каркас с тегом <div id="root"></div>, и парсинг завершается неудачей.
Второй шаг — подключить headless-браузер (Playwright, Puppeteer или Selenium). Страница отрисовывается корректно, но возникает фундаментальная проблема подготовки данных для LLM:
- Избыточный расход токенов. Типичная веб-страница весит от сотен килобайт до нескольких мегабайт в виде сырого DOM-дерева, тогда как полезного текста там обычно не более нескольких абзацев. Передача сырого HTML в промпт быстро исчерпывает контекст модели и увеличивает накладные расходы на обработку токенов в разы.
- Галлюцинации и шум. Тексты навигационных меню («Главная», «О компании», «Политика конфиденциальности»), ссылки в футере и всплывающие баннеры путают семантический поиск: векторная база начинает находить пункт меню вместо ответа на вопрос пользователя.
- Утечки памяти браузера. Headless Chromium проектировался для тестирования верстки, а не для круглосуточного промышленного парсинга сотен страниц в час. Без тонкого управления пулом контекстов процесс за считанные часы забивает всю оперативную память VDS и падает от OOM Killer.
- Зависимость от облачных SaaS. Популярные проприетарные парсеры (Firecrawl, Jina) требуют регулярной оплаты в валюте и несут риски нарушения 152-ФЗ, так как парсинг клиентских каталогов или внутренних документов происходит на сторонней зарубежной инфраструктуре.
| Инструмент | Рендеринг JS (SPA) | Формат на выходе | Очистка от мусора | Готовность для RAG |
|---|---|---|---|---|
| BeautifulSoup / Cheerio | Нет (только статика) | Сырой HTML | Вручную (регулярки/селекторы) | Низкая |
| Чистый Playwright | Да (полный движок) | Полный DOM (мегабайты) | Требует кастомных скриптов | Средняя (нужна постобработка) |
| Облачные парсеры (Firecrawl) | Да | Markdown / JSON | Автоматическая | Высокая, но привязка к подписке |
| Crawl4AI v0.9.5 | Да (асинхронный пул) | Чистый Markdown / Чанки | Встроенные алгоритмы Fit & BM25 | Высокая (автономная работа на VDS) |
2. Архитектура Crawl4AI: как устроен краулер и режимы экстракции
Проект unclecode/crawl4ai распространяется по свободной лицензии Apache-2.0. Под капотом сервер построен на базе современного веб-фреймворка FastAPI, библиотеки асинхронного управления браузером Playwright и конвейера эвристической очистки контента.
В отличие от стандартного браузера, запускающего новую вкладку на каждый чих, Crawl4AI использует архитектуру пула браузеров (Browser Pool). Движок держит инициализированные контексты Chromium в «горячем» состоянии, изолирует сетевые запросы и поддерживает 4 специализированных режима извлечения контента:
- Режим
fit(по умолчанию): использует алгоритмы Readability для отсечения второстепенных блоков страницы (меню, сайдбары, копирайты, плашки cookie). На выходе остаётся только смысловой текст статьи или описания товара, форматированный в строгий Markdown с сохранением заголовков, списков и таблиц. - Режим
raw: выполняет прямую конвертацию всего дерева элементов DOM в Markdown без смысловой фильтрации. Полезен, когда необходимо захватить метаданные, шапку или структуру всего портала. - Режим
bm25: ранжирует блоки текста по алгоритму поисковой релевантности BM25 относительно переданного поискового запросаquery. Это позволяет извлечь со страницы объемом 100 страниц только те абзацы, которые прямо отвечают на конкретный вопрос. - Режим
llm: отправляет очищенный текст в подключенную языковую модель (локальную Ollama или внешнюю сеть) для суммаризации или структурирования данных по JSON-схеме прямо «на лету» перед возвратом ответа.
💡 Совет инженера: почему Markdown предпочтительнее JSON для RAG
Языковые модели изначально обучаются на массивах Markdown-текстов из GitHub, документаций и книг. Разметка Markdown (заголовки #, ##, списки и таблицы) несёт строгую семантику структуры документа, при этом тратит в 3–5 раз меньше служебных токенов, чем сложные вложенные структуры JSON или HTML-теги.
3. Требования к серверу: CPU, RAM и тонкости разделяемой памяти Chromium
В отличие от инференса больших языковых моделей, Crawl4AI не требует видеокарты (GPU): рендеринг веб-страниц и конвертация DOM выполняются на центральном процессоре. Сервер запускает Chromium с флагами --disable-gpu и --disable-software-rasterizer, что позволяет разворачивать его на стандартных бюджетных VDS.
Минимальные и рекомендуемые характеристики VDS
| Параметр | Минимальный тариф (1 поток) | Рекомендуемый тариф (Production) |
|---|---|---|
| Процессор (vCPU) | 1–2 vCPU (от 2.4 ГГц) | 2–4 vCPU (от 3.3 ГГц NVMe) |
| Оперативная память (RAM) | 2 ГБ (с лимитом пула до 1–2 вкладок) | 4–8 ГБ (пул до 10–20 параллельных страниц) |
| Диск (NVMe) | 20 ГБ | 40–60 ГБ (с запасом под кэш страниц и логи) |
| Разделяемая память (shm_size) | 512 МБ | 1 ГБ (обязательно в Compose) |
Коварная ловушка Docker: падение Chromium из-за /dev/shm
Главная ошибка, с которой сталкиваются 9 из 10 инженеров при первом запуске Playwright или Crawl4AI в контейнере — внезапные падения процессов с ошибкой Target crashed или сигналом SIGBUS.
Причина кроется в архитектуре Chromium: браузер активно использует системную разделяемую память (shared memory, смонтированную в /dev/shm) для обмена графическими буферами и тяжелыми объектами между процессами вкладок и рендерера. По умолчанию Docker выделяет под /dev/shm всего 64 мегабайта. Стоит краулеру открыть тяжелый портал с десятками картинок или сложными анимациями, лимит в 64 МБ мгновенно исчерпывается, и ядро Linux принудительно убивает процесс браузера.
Для устранения проблемы в манифесте Compose обязательно объявляется директива shm_size: "1gb", а в конфигурацию Chromium передаётся флаг --disable-dev-shm-usage.
4. Развертывание в Docker Compose с защитой портов и лимитами ресурсов
Сервер Crawl4AI по умолчанию слушает порт 11235. Если выставить этот порт наружу без авторизации, ваш VDS мгновенно превратят в открытый анонимный прокси для нелегального скрапинга и DDoS-атак. Поэтому развертывание строится по трём правилам информационной безопасности SysKit:
- Привязка порта только к локальной петле (
127.0.0.1:11235:11235). Это исключает доступ из внешней сети и защищает от прямого обхода правил UFW (механику этой уязвимости мы подробно разбирали в статье Docker обходит UFW на Linux). - Обязательная авторизация по токену
CRAWL4AI_API_TOKEN. Встроенный охранник безопасности сервиса отказывается обрабатывать внешние запросы без валидного Bearer-токена. - Жесткие аппаратные лимиты (
mem_limitиcpus). Защищают операционную систему хоста от зависания при резком наплыве сложных страниц.
Генерация секретного токена и подготовка каталога
# Создаём рабочий каталог для проекта
mkdir -p /opt/crawl4ai && cd /opt/crawl4ai
# Генерируем надёжный криптографический токен (32 байта hex)
# Можно сгенерировать токен утилитой: https://syskit.ru/password-generator
CRAWL_TOKEN=$(openssl rand -hex 32)
echo "Сгенерирован токен: ${CRAWL_TOKEN}"
# Сохраняем переменные в защищенный файл .env
cat <<EOF > .env
CRAWL4AI_API_TOKEN=${CRAWL_TOKEN}
CRAWL4AI_ALLOW_DEEP_CRAWL=false
EOF
chmod 600 .env
💡 Совет инженера: пароли и ключи безопасности
Для генерации стойких токенов авторизации микросервисов удобно использовать встроенный генератор надёжных паролей SysKit. Выбирайте длину от 32 символов без спецсимволов, чувствительных к экранированию в bash.
Манифест docker-compose.yml
services:
crawl4ai:
image: unclecode/crawl4ai:0.9.5
container_name: crawl4ai
restart: unless-stopped
env_file:
- .env
environment:
- CRAWL4AI_API_TOKEN=${CRAWL4AI_API_TOKEN}
- CRAWL4AI_ALLOW_DEEP_CRAWL=${CRAWL4AI_ALLOW_DEEP_CRAWL:-false}
ports:
# Защита от прямого сканирования: слушаем строго 127.0.0.1
- "127.0.0.1:11235:11235"
# Критично для стабильности Chromium: увеличиваем shm до 1 ГБ
shm_size: "1gb"
# Безопасность: отключаем эскалацию привилегий
security_opt:
- no-new-privileges:true
# Лимиты ресурсов для защиты сервера от OOM Killer
mem_limit: 3g
cpus: 2.0
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
healthcheck:
test: ["CMD-SHELL", "curl -fsS http://127.0.0.1:11235/health || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 20s
Запуск и верификация работоспособности
# Запуск контейнера в фоновом режиме
docker compose up -d
# Проверяем статус и прохождение проверки здоровья
docker compose ps
# Смотрим логи инициализации пула браузеров
docker compose logs --tail=50 -f crawl4ai
При успешном старте в логах появится сообщение о готовности FastAPI и успешной инициализации пула Chromium. Проверить доступность службы на локальной петле можно запросом к системному эндпоинту:
# Проверка healthcheck контейнера
curl -s http://127.0.0.1:11235/health
# Ожидаемый ответ: {"status":"ok","timestamp":1729760000.0,"version":"0.9.5"}
5. Практическая работа с REST API: эндпоинты /md, /crawl и авторизация по токену
В версиях линейки v0.9.x разработчики Crawl4AI выделили специальный облегченный эндпоинт POST /md. Он спроектирован для мгновенной конвертации одиночных URL прямо в формат Markdown без необходимости передавать громоздкие JSON-объекты конфигурации.
Быстрая выгрузка Markdown через cURL
# Экспортируем токен для удобства тестирования
export CRAWL_TOKEN="ваш_сгенерированный_токен_из_env"
# Базовый запрос: извлечение чистого контента с фильтрацией (режим "fit")
curl -s -X POST http://127.0.0.1:11235/md \
-H "Authorization: Bearer ${CRAWL_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"url": "https://syskit.ru",
"f": "fit"
}' | jq .markdown -r | head -n 30
Обратите внимание на параметр "f": "fit": он указывает движку применить алгоритмы Readability и выбросить из разметки всё, что не относится к основному содержанию. Если вам требуется найти конкретные факты на многостраничном документе, задействуйте режим bm25 с фильтрацией по ключевым словам:
# Поиск и извлечение только релевантных абзацев по алгоритму BM25
curl -s -X POST http://127.0.0.1:11235/md \
-H "Authorization: Bearer ${CRAWL_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"url": "https://habr.com/ru/articles/800000/",
"f": "bm25",
"q": "Docker Compose лимиты памяти"
}' | jq .markdown -r
Универсальный Python-скрипт интеграции
Для включения краулера в состав бэкенд-сервисов или AI-пайплайнов достаточно стандартной библиотеки httpx или requests:
import os
import requests
CRAWL_URL = "http://127.0.0.1:11235/md"
API_TOKEN = os.getenv("CRAWL4AI_API_TOKEN", "ваш_токен")
def fetch_clean_markdown(target_url: str, mode: str = "fit") -> str:
"""Извлекает чистый Markdown для последующей передачи в RAG/LLM."""
headers = {
"Authorization": f"Bearer {API_TOKEN}",
"Content-Type": "application/json"
}
payload = {
"url": target_url,
"f": mode,
"c": False # c=True включает кэширование повторных запросов
}
response = requests.post(CRAWL_URL, json=payload, headers=headers, timeout=60)
response.raise_for_status()
data = response.json()
if not data.get("success"):
raise RuntimeError(f"Ошибка скрапинга: {data}")
return data.get("markdown", "")
if __name__ == "__main__":
url = "https://syskit.ru"
md_content = fetch_clean_markdown(url)
print(f"Успешно получено {len(md_content)} символов чистого Markdown:")
print(md_content[:400] + "\n...")
Интерактивный веб-плейграунд (Web Playground)
Crawl4AI поставляется со встроенным визуальным интерфейсом для отладки селекторов и проверки результатов краулинга на порту /playground. Так как сервис привязан к локальной петле сервера, открывать его наружу в интернет небезопасно. Самый правильный инженерный способ доступа — создание защищённого SSH-туннеля с рабочего компьютера:
# Пробрасываем порт 11235 сервера на локальный ПК через SSH
ssh -L 11235:127.0.0.1:11235 root@203.0.113.10
После подключения откройте в браузере локальный адрес http://127.0.0.1:11235/playground. В веб-интерфейсе можно визуально настроить параметры CrawlerRunConfig, указать селекторы CSS или XPath, отладить время ожидания отрисовки JS-элементов и в один клик получить готовый JSON для передачи в API.
Рекомендуемые VDS-провайдеры
Отказоустойчивые сервера с быстрыми NVMe и каналом до 1 Гбит/с
Timeweb Cloud — Быстрые NVMe VDS
Высокочастотные процессоры 3.3 ГГц и быстрые NVMe для параллельного рендеринга страниц в Chromium без задержек и троттлинга.
Selectel — Инфраструктура Tier III
Надежная серверная база в дата-центрах РФ уровня Tier III, приватные подсети и выделенные ресурсы под задачи корпоративного RAG.
Beget — Простой старт для домена
Удобная панель управления, моментальное развертывание Docker на Ubuntu 24.04 и почасовая тарификация для тестовых краулеров.
6. Интеграция с локальным RAG: от сырых веб-страниц до векторных чанков
Главное преимущество использования Markdown на выходе Crawl4AI — естественное семантическое разбиение на чанки (Chunking). Когда вы режете сырой HTML, теги <div> и <p> разрываются посередине, ломая контекст для эмбеддинг-модели. Разметка Markdown содержит логическую иерархию заголовков (#, ##, ###), что позволяет разбивать документ строго по смысловым блокам.
Схема RAG-пайплайна на базе Crawl4AI
- Загрузка и парсинг: Crawl4AI загружает SPA-страницу через Headless Chromium, исполняет JS-скрипты и отдаёт очищенный Markdown через
POST /md. - Семантическое чанкование: Текст нарезается рекурсивным разделителем (например, по заголовкам второго уровня
##и абзацам) с размером чанка 500–1000 символов и перекрытием (overlap) 100 символов. - Генерация векторных эмбеддингов: Чанки отправляются в легковесную модель эмбеддингов (например,
bge-m3илиnomic-embed-text, развернутую в локальной Ollama). - Индексация в векторной БД: Векторы сохраняются в Qdrant или PostgreSQL с расширением
pgvectorдля быстрого косинусного поиска.
# Пример рекурсивного чанкования Markdown для RAG-системы
def split_markdown_into_chunks(markdown_text: str, chunk_size: int = 800, overlap: int = 100) -> list[str]:
"""Разбивает Markdown по границам заголовков и абзацев без разрыва слов."""
import re
# Разделяем по заголовкам второго и третьего уровня
raw_sections = re.split(r'\n(?=#{1,3}\s)', markdown_text)
chunks = []
for section in raw_sections:
section = section.strip()
if not section:
continue
if len(section) <= chunk_size:
chunks.append(section)
else:
# Длинную секцию дробим по абзацам
paragraphs = section.split('\n\n')
current_chunk = ""
for p in paragraphs:
if len(current_chunk) + len(p) < chunk_size:
current_chunk += p + "\n\n"
else:
if current_chunk:
chunks.append(current_chunk.strip())
current_chunk = p + "\n\n"
if current_chunk:
chunks.append(current_chunk.strip())
return chunks
При таком подходе каждый фрагмент базы знаний содержит точный смысловой заголовок секции, что существенно улучшает качество семантического поиска (Retrieval Precision), сохраняет логическую структуру документа и защищает языковую модель от галлюцинаций из-за фрагментированного текста.
7. Безопасность и оптимизация: ротация контекстов Chromium и борьба с OOM Killer
В долгоживущих сервисах скрапинга главной проблемой остаётся деградация производительности браузера. Chromium накапливает во внутреннем контексте кэш стилей, service workers и куки посещенных сайтов. В результате к 500-й странице потребление памяти одним процессом может вырасти с 200 МБ до 1.5 ГБ.
Встроенный механизм ротации контекстов (Context Recycling)
В официальной архитектуре Crawl4AI предусмотрен параметр max_pages_before_recycle. По умолчанию он установлен на 200 страниц. Когда браузерный контекст обслуживает 200 запросов, он плавно вытесняется и пересоздается внутри того же процесса Chromium без перезапуска контейнера и без простоя входящих соединений.
Ограничение сетевого доступа через Nginx
Если краулер должен принимать задания из внешних сетей (например, от удаленного вебхука или CRM), перед ним необходимо поставить реверс-прокси Nginx или Caddy с SSL-сертификатом:
# Зона ограничения запросов (объявляется в секции http или в файле виртуального хоста)
limit_req_zone $binary_remote_addr zone=crawl_limit:10m rate=5r/s;
server {
listen 443 ssl http2;
server_name crawl.example.ru;
ssl_certificate /etc/letsencrypt/live/crawl.example.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/crawl.example.ru/privkey.pem;
# Ограничение размера тела запроса (защита от DoS)
client_max_body_size 10M;
# Лимитирование частоты запросов
limit_req zone=crawl_limit burst=15 nodelay;
location / {
proxy_pass http://127.0.0.1:11235;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Таймауты для тяжелых страниц с долгим JS-рендерингом
proxy_read_timeout 120s;
proxy_connect_timeout 60s;
}
}
Проверить корректность настройки SSL-сертификата и заголовков безопасности можно через сервис проверки SSL-сертификатов и инструмент анализа HTTP-заголовков безопасности.
8. Мониторинг, бэкап конфигурации и обновление версий
Поскольку Crawl4AI является stateless-микросервисом (хранит состояние только в оперативной памяти и временном кэше), стратегия резервного копирования сводится к сохранению конфигурации и переменных окружения:
#!/usr/bin/env bash
set -euo pipefail
BACKUP_DIR="/var/backups/crawl4ai"
STAMP="$(date +%Y%m%d-%H%M%S)"
ARCHIVE="${BACKUP_DIR}/crawl4ai-config-${STAMP}.tar.gz"
mkdir -p "${BACKUP_DIR}"
# Архивируем конфигурацию и секреты
tar -czf "${ARCHIVE}" -C /opt/crawl4ai docker-compose.yml .env
# Проверяем целостность архива
tar -tzf "${ARCHIVE}" >/dev/null
echo "Создан бэкап конфигурации: ${ARCHIVE}"
# Ротация локальных архивов: храним 14 дней
find "${BACKUP_DIR}" -name 'crawl4ai-config-*.tar.gz' -mtime +14 -delete
Для долгосрочного хранения архивов настройте синхронизацию с российским объектным хранилищем по нашему гайду по автоматическим шифрованным бэкапам VDS в S3.
Мониторинг доступности и системных ресурсов
Для контроля состояния краулера используются два уровня наблюдения:
- Контроль потребления памяти и CPU контейнера: реальный объем RAM и нагрузку на процессор надежнее всего контролировать встроенной командой
docker stats, отслеживая отсутствие приближения к лимитуmem_limit: 3g. - HTTP-метрики Prometheus: при включенном блоке observability в конфигурации сервер экспортирует стандартные метрики библиотеки
prometheus-fastapi-instrumentator(счетчикиhttp_requests_totalи гистограммы задержекhttp_request_duration_seconds). Обратите внимание: доступ к эндпоинту/metricsзащищен Bearer-токеном.
# Мониторинг реального потребления памяти и процессора в реальном времени
docker stats --no-stream crawl4ai
# Сбор HTTP-метрик с передачей Bearer-токена
curl -s -H "Authorization: Bearer ${CRAWL_TOKEN}" http://127.0.0.1:11235/metrics | grep http_requests_total
Процедура обновления до свежей версии
# Переходим в рабочий каталог
cd /opt/crawl4ai
# Загружаем обновленный образ
docker compose pull
# Перезапускаем контейнер без простоя
docker compose up -d
# Проверяем успешный старт
docker compose ps
docker compose logs --tail=30 crawl4ai
9. Чек-лист проверки готовности Crawl4AI к промышленной нагрузке
| Параметр | Требование безопасности / надежности | Назначение |
|---|---|---|
| Разделяемая память | shm_size: "1gb" |
Предотвращение падений Chromium по SIGBUS |
| Сетевой порт 11235 | 127.0.0.1:11235:11235 |
Защита от обхода UFW и сканирования из интернета |
| Токен авторизации | Задан в CRAWL4AI_API_TOKEN (32+ байт) |
Защита API от несанкционированных запросов |
| Лимиты Docker | mem_limit: 3g, cpus: 2.0 |
Изоляция от зависания операционной системы хоста |
| Проверка здоровья | Healthcheck по эндпоинту /health |
Автоматический перезапуск при зависании процессов |
| Ротация вкладок | max_pages_before_recycle: 200 |
Устранение утечек памяти в длительных сессиях |
Развертывание Crawl4AI на собственном VDS превращает задачу подготовки данных для RAG и ИИ-агентов из дорогостоящей подписки в надежный, подконтрольный инженерный процесс. Сервер выдает идеально структурированный Markdown, не требует дорогостоящих видеокарт и легко интегрируется в любые существующие пайплайны на Python или n8n.
🎯 Подберите производительный VDS для краулинга и RAG
Для параллельной работы нескольких контекстов Chromium критичны быстрые ядра процессора и достаточный объем оперативной памяти. Выбирайте надежных российских провайдеров с быстрыми NVMe-дисками для стабильного парсинга без троттлинга.