Обхід
Yozh Crawler обходить сайт, починаючи з одного вихідного URL-адреси, і передає кожну виявлену сторінку через SSE. Він відповідає за процес виявлення — визначення меж, дедуплікацію, визначення області пошуку, вилучення посилань, повторні спроби та дотримання правил ввічливості — тоді як завантаження кожної сторінки делегується Yozh Scraper через HTTP. Для роботи використовується лише один стек Playwright, і всі функції скрепера (проксі, прихований режим, сесії, правила вилучення даних) застосовуються також і до проіндексованих сторінок.
- Предикати області дії —
same-domain/subdomains/all/regex, з шаблонами включення та виключення та жорсткимиmax_depth/max_pagesобмеженнями. - Дедуплікація за «відбитками» — URL-адреси канонізуються, а потім хешуються (SHA1), завдяки чому запити з іншою послідовністю та варіанти з кінцевим слешем об’єднуються в одне відвідування.
- Принцип «round-robin» на рівні домену — один повільний або величезний хост не може позбавити інших робочих процесів.
- Адаптивний обмежувач швидкості — глобальний «кошик токенів» (з джиттером) для кожного домену, спільний для всіх завдань; це
429зменшує RPS удвічі на час періоду охолодження. - Стан сеансу — система оцінювання за методом Crawlee:
401/403/429завершення сеансу, помилки підвищують його оцінку, успішні дії — знижують; ротація відбувається при оцінці ≥ 3 або після 50 використань. - Потокове передавання SSE — кожне завдання генерує
stats,page,page_error, а також подіїdone/cancelled. Реагуйте на знайдені дані одразу ж після їх надходження.
:8000 , а сканер — на :8001.Швидкий старт
Краулер підключений до кореня docker-compose.yml і запускається разом зі скрейпером:
docker compose up --build # scraper → http://localhost:8000 # crawler → http://localhost:8001
Або запустіть його локально без Docker — вкажіть SCRAPER_URL на запущений скрейпер:
cd yozh-crawler pip install -r requirements.txt cp .env.example .env # edit SCRAPER_URL if needed python -m uvicorn src.main:app --reload --port 8001
Основні принципи використання
Надіслати запит на сканування з seed_url та scope. Виклик негайно повертає job_id ; після цього сканування виконується у фоновому режимі за допомогою робочих завдань.
curl -X POST http://localhost:8001/api/v1/crawl
-H "Content-Type: application/json"
-d '{
"seed_url": "https://example.com",
"scope": {"mode": "same-domain", "max_depth": 2, "max_pages": 50,
"per_domain_rps": 1.0, "per_domain_concurrency": 1},
"scrape_options": {"proxy_type": "none"},
"enable_scraping": false
}'
Відповідь — це лише ідентифікатор завдання:
{ "job_id": "crawl_abc123" }
Звідси ви можете або дивитися прямі трансляції подій, або завантажити повний запис.
«Відкриття» проти «збору» — enable_scraping перемикач
Один і той самий механізм сканування працює в обох напрямках. Один логічний оператор визначає вибір між «економічним» скануванням для створення карти та повним збором даних — і кожен режим використовує власну конфігурацію проксі-сервера, тому взаємовплив між ними відсутній.
Карта відкриттів
Поверхневий обхід. Скрейпер як і раніше обробляє кожну сторінку (тож посилання, створені за допомогою JS, виявляються), але сканер зберігає лише основну структуру — це ідеально підходить для створення карт сайту, аудиту посилань та виявлення змін.
- Зберігає
url·parent_url·depth·status·took_ms - Додає необроблений HTML-код, скріншоти та витягнуті дані
- Використовує дешевший
crawl_proxy - Найменші корисні навантаження, найшвидше сканування
Повний збір
Кожна відвідана сторінка зберігається разом із повним ScrapeResponse — вихідним HTML-кодом, опціональним знімком екрана та будь-якими полями з ваших правил вилучення. Сканування та структурування за один прохід.
- Зберігає повний
ScrapeResponseна сторінці -
raw_html·screenshot· витягнутоdata - Застосування
scrape_options.proxy_* - Перейти
extractправила для отримання чистого JSON
crawl_proxy використовується лише тоді, коли enable_scraping=false; scrape_options.proxy_* використовується, коли true. Якщо crawl_proxy є null, то scrape_options проксі використовується незалежно від режиму.Обсяг індексації
Область дії визначає межі сканування: де дозволено переміщатися та наскільки інтенсивно можна завантажувати кожен хост. Це scope об’єкт у запиті.
| Поле | Тип | За замовчуванням | Опис |
|---|---|---|---|
mode | same-domain · subdomains · all · regex | same-domain | Які посилання входять до сфери застосування. regex відповідає include_patterns. |
include_patterns | string[] | [] | Регулярні вирази, яким повинен відповідати URL-адреса для включення в чергу (використовується в regex режимом / як список дозволених адрес). |
exclude_patterns | string[] | [] | Регулярні вирази, які виключають URL-адресу, навіть якщо вона в іншому випадку потрапляє в область дії. |
max_depth | int | 3 | Максимальна глибина зв’язку від початкового вузла (початковий вузол має глибину 0). |
max_pages | int | 500 | Максимальна кількість сторінок, які можна відвідати до завершення сканування. |
per_domain_rps | float | 1.0 | Швидкість поповнення «кошика токенів» для кожного домену (запити/секунду). |
per_domain_concurrency | int | 1 | Максимальна кількість запитів під час сеансу до одного домену. |
subdomains. Вона використовує останні дві мітки як домен, що підлягає реєстрації, тому відбувається надмірне співпадіння з хостами зі списку публічних суфіксів, такими як github.io або co.uk. Краще використовувати same-domain або regex для таких випадків.Результати потокової передачі (SSE)
Кожне завдання надає доступ до потоку Server-Sent Events. Використовуйте його, щоб обробляти сторінки одразу після їх виявлення, замість того щоб чекати завершення сканування.
curl -N http://localhost:8001/api/v1/crawl/crawl_abc123/events
Потік генерує п’ять типів подій:
| Подія | Коли |
|---|---|
stats | Періодичний знімок стану виконання (відвідано / у черзі / невдало / пропущено через дублювання / поза межами сфери застосування / повторні спроби). |
page | Один запис на кожну відвідану URL-адресу. Містить повні ScrapeResponse у режимі збору даних, скорочені метадані — у режимі пошуку. |
page_error | URL-адреса, для якої всі спроби підключення завершилися невдачею. |
done | Термінал — сканування завершилося успішно. |
cancelled | Термінал — сканування було скасовано. |
Подія page подія в режимі виявлення виглядає так:
{
"event": "page",
"url": "https://example.com/docs/intro",
"parent_url": "https://example.com/docs",
"depth": 2,
"status_code": 200,
"took_ms": 812,
"scrape_response": null
}
Стан та результати
Віддаєте перевагу опитуванню? Запис про завдання можна переглянути в будь-який момент — у тому числі під час сканування, з урахуванням сторінок, виявлених на цей момент. /results є псевдонімом кінцевої точки завдання, який збережено для симетрії зі скрепером.
curl http://localhost:8001/api/v1/crawl/crawl_abc123 curl http://localhost:8001/api/v1/crawl/crawl_abc123/results
{
"job_id": "crawl_abc123",
"status": "running",
"stats": {
"visited": 47, "queued": 12, "failed": 0,
"dedup_skipped": 9, "out_of_scope": 31, "retries_total": 2
},
"pages": [
{ "url": "https://example.com/", "parent_url": null,
"depth": 0, "status_code": 200, "took_ms": 640 }
]
}
status проходить через queued → running → done (або failed / cancelled).Скасування сканування
Зупиніть завдання, що виконується, за допомогою DELETE. М'яке скасування є стандартним варіантом і найбезпечнішим вибором.
# soft — stop scheduling, let in-flight requests drain curl -X DELETE "http://localhost:8001/api/v1/crawl/crawl_abc123?hard=false" # hard — abort in-flight asyncio tasks immediately curl -X DELETE "http://localhost:8001/api/v1/crawl/crawl_abc123?hard=true"
?hard=true призводить до відміни поточного запиту сканера, але сам сканер не має можливості про це дізнатися — рендеринг сторінки завершується з його боку. Надавайте перевагу м'якому скасуванню, якщо тільки вам не потрібно зупинитися негайно.MCP
fastapi-mcp встановлено за адресою /mcp як кінцева точка Streamable HTTP. Наведіть Claude або Cursor на неї, і інструменти з’являться автоматично:
healthcreate_crawlget_crawlget_crawl_resultscancel_crawl
"open-crawler": {
"type": "http",
"url": "http://localhost:8001/mcp"
}
stream_crawl_events навмисно виключено з MCP — потокові відповіді не сумісні з інструментом типу «запит/відповідь».Довідник з налаштування
Запит — CrawlRequest
| Поле | Тип | За замовчуванням | Опис |
|---|---|---|---|
seed_url | рядок (URL) | обов’язкове | Єдиний URL-адресу, з якої починається сканування. |
scope | CrawlScope | defaults | Межі та ввічливість — див. «Область дії сканування». |
scrape_options | ScrapeOptions | defaults | Передається дослівно до скрепера по сторінці (url вставляється). |
crawl_proxy | ScrapeOptions · null | null | Дешевий проксі, що використовується в режимі виявлення. Читаються лише proxy_type / proxy_pool_id / proxy_geo зчитуються. |
enable_scraping | bool | false | Вкажіть повну кількість ScrapeResponse на кожній сторінці (true) або лише метадані пошуку (false). |
За сторінку — ScrapeOptions (вибрано)
| Поле | Тип | За замовчуванням | Опис |
|---|---|---|---|
proxy_type | none · mobile · res_static · res_rotating · dc_static · … | none | Пул проксі-серверів, через який здійснюється завантаження сторінок. |
device | desktop · mobile | desktop | Вікно перегляду / профіль UA. |
render | bool | true | Відобразити за допомогою браузера (необхідно для посилань, створених за допомогою JS). |
stealth | bool | true | Застосуйте заходи з посилення захисту від виявлення. |
wait_until | domcontentloaded · networkidle | domcontentloaded | Коли сторінка вважається готовою. |
screenshot | bool | false | Зробити знімок екрана (режим «збору»). |
extract | ExtractRule · null | null | Правила для полів CSS/XPath → структуровані data на кожній сторінці. |
session_id | рядок · null | null | Повторне використання сеансу скрепера з автентифікацією — див. розділ «Скрейпінг з автентифікацією». |
Змінні середовища
| Змінна | За замовчуванням | Примітки |
|---|---|---|
SCRAPER_URL | http://web-scraper:8000 | Скрепер на вході (використовуйте ім’я служби Docker у мережі Compose). |
WORKERS | 2 | Кількість паралельних завдань індексації. |
QUEUE_MAXSIZE | 200 | Глибина черги завдань, що очікують на виконання. |
JOB_TIMEOUT_MS | 3_600_000 | Обмеження тривалості одного завдання, що виконується у фоновому режимі, у вигляді «стінного годинника». |
SCRAPER_JOB_TIMEOUT_MS | 120_000 | Тайм-аут запиту скрепера на одну сторінку. |
MAX_RETRIES | 3 | Кількість повторних спроб на один запит у разі тимчасової помилки. |
RETRY_HTTP_CODES | [408,429,500,502,503,504] | Коди статусу, які викликають повторну спробу. |
RETRY_BACKOFF_MAX | 30.0 | Верхня межа експоненціального відступу (секунди). |
SESSION_MAX_ERROR_SCORE | 3.0 | Поріг завершення сеансу. |
SESSION_MAX_USAGE | 50 | Оновити сесію після N використань. |
SESSION_BLOCKED_CODES | [401,403,429] | Коди, які миттєво завершують сеанс. |
Довідник API
| Метод | Шлях | Призначення |
|---|---|---|
| POST | /api/v1/crawl | Створити завдання. Повертає {"job_id":"…"}. |
| GET | /api/v1/crawl/{id} | Запис про роботу — статус, статистика, кількість переглянутих сторінок. |
| GET | /api/v1/crawl/{id}/results | Псевдонім запису про вакансію. |
| GET | /api/v1/crawl/{id}/events | Потік SSE (stats/page/page_error/done/cancelled). |
| DELETE | /api/v1/crawl/{id}?hard=bool | Скасувати завдання (м’яке або жорстке скасування). |
| GET | /api/v1/health | Здоров’я + доступність скрепера. |
Важливі деталі
robots.txt не враховується. Пошуковий робот обходить усі URL-адреси, що входять до сфери дії, незалежно від /robots.txt. Ви несете відповідальність за дотримання правил файлу robots.txt та умов використання сайту щодо об’єктів, які вам не належать — використовуйте exclude_patterns та обмеження швидкості, щоб дотримуватися етикету.POST /sessions + POST /sessions/{id}/login), а потім передайте scrape_options.session_id. Скрейпер передає кукі та стан сховища в обох напрямках для кожного запиту, тому кожна просканована сторінка бачить стан з автентифікацією.