# Dubizzle scraper Сервис для сбора объявлений с `dubizzle.com`. Основной сценарий работы: - discovery списка машин через `Algolia` - при необходимости fallback на сайт - нормализация полей автомобиля - upsert в PostgreSQL - периодический запуск через `Celery Beat` ## Что делает проект Проект: - собирает все доступные машины из каталога `Dubizzle` - использует `Algolia` как основной источник discovery - обходит лимит одного запроса через сегментацию каталога - сохраняет автомобили и изображения в PostgreSQL - отдает API для просмотра данных и запуска задач - поддерживает hourly/full scan через `Celery` ## Текущая архитектура Сейчас проект настроен под `Dubizzle`. Ключевые особенности: - пакет проекта: `dubizzle_scraper` - режим discovery по умолчанию: `algolia` - full scan включен по умолчанию - авто-сегментация: `DUBIZZLE_LISTING_SEGMENTS=auto` - параллельный запуск сегментов: `CELERY_PARALLEL_SEGMENTS=true` ### Как собирается весь каталог Один широкий запрос в `Algolia` упирается примерно в лимит $10{,}000$ доступных результатов. Поэтому проект использует сегментацию по годам. Авто-сегменты (`auto`) сейчас разбивают каталог на 8 диапазонов по году: - `1900-2012` - `2013-2015` - `2016-2017` - `2018-2019` - `2020-2021` - `2022-2023` - `2024-2025` - `2026-2027` Если сегмент все равно слишком большой, discovery дополнительно режет его по `id`-диапазонам. ## Стек - `Python 3.11+` - `Playwright` - `FastAPI` - `Celery` - `Redis` - `PostgreSQL` - `SQLAlchemy` - `Alembic` - `Docker Compose` ## Быстрый старт через Docker Это основной рекомендуемый способ запуска. ### 1. Подготовить окружение ```bash cp .env.example .env ``` Если нужно, отредактируй `.env`. ### 2. Запустить стек ```bash docker compose up -d ``` Поднимутся сервисы: - `postgres` - `redis` - `migrate` - `api` - `worker` - `beat` ### 3. Проверить статус ```bash docker compose ps ``` API будет доступен по адресу: - `http://localhost:18000` - Swagger: `http://localhost:18000/docs` > Порт пробрасывается как `${DUBIZZLE_API_HOST_PORT:-18000}:8000`. ## Полный сброс и чистый старт Если нужен запуск с нуля с чистой БД и чистым Redis: ```bash docker compose down -v --remove-orphans docker compose up -d --build ``` Важно: - `docker compose down` **не удаляет volume** - `docker compose down -v` удаляет данные `PostgreSQL` и `Redis` ## Основные Docker-сервисы ### `postgres` PostgreSQL база проекта. По умолчанию: - DB: `dubizzle_scraper` - user: `dubizzle` - password: `dubizzle` - host port: `15432` ### `redis` Используется как: - broker для `Celery` - backend для task state - хранилище progress/state ключей По умолчанию host port: `16379` ### `migrate` Отдельный сервис, который выполняет: ```bash alembic upgrade head ``` ### `api` Запускает: ```bash uvicorn dubizzle_scraper.api.app:app --host 0.0.0.0 --port 8000 ``` ### `worker` Запускает `Celery worker` и выполняет scraping-задачи. ### `beat` Планировщик periodic tasks. По умолчанию проект настроен на запуск каждые `60` минут. ## Ручной запуск задач в Docker ### Разовый sync listing ```bash docker compose run --rm --profile manual sync ``` По умолчанию это запустит: ```bash dubizzle sync-listing --limit 100 --output /app/artifacts/json/docker_sync_listing.json ``` Результат будет на хосте в: - `artifacts/json/docker_sync_listing.json` ### Запуск с другим лимитом ```bash docker compose run --rm --profile manual -e DUBIZZLE_SYNC_LISTING_LIMIT=500 sync ``` ### Запуск непрерывного режима ```bash docker compose up -d api worker beat ``` ## Локальный запуск без Docker Локальный запуск возможен, но проект в первую очередь ориентирован на `PostgreSQL + Redis`. ### Установка ```bash pip install -e . playwright install chromium ``` ### Миграции ```bash alembic upgrade head ``` ### Запуск API ```bash uvicorn dubizzle_scraper.api.app:app --reload --port 8000 ``` ### Запуск worker ```bash celery -A dubizzle_scraper.worker.celery_app worker --loglevel=info --concurrency=1 --pool=solo -Q scraping ``` ### Запуск beat ```bash celery -A dubizzle_scraper.worker.celery_app beat --loglevel=info ``` ### Полный sync листинга ```bash dubizzle sync-listing --limit 100 ``` Если включена сегментация и не переданы `make`, `model`, `limit`, CLI автоматически пойдет в segmented sync. ## API Базовые роуты: ### Health - `GET /health` ### Машины - `GET /api/v1/cars` - `GET /api/v1/cars/{car_id}` - `GET /api/v1/cars/by-origin/{origin_id}` - `GET /api/v1/stats` ### Задачи - `POST /api/v1/tasks/sync-vehicle` - `POST /api/v1/tasks/sync-listing` - `GET /api/v1/tasks/{task_id}` - `GET /api/v1/sync-runs` ### Пример запуска sync vehicle ```bash curl -X POST http://localhost:18000/api/v1/tasks/sync-vehicle \ -H "Content-Type: application/json" \ -d '{"vehicle_url": "https://www.dubizzle.com/VehicleDetail/45089484~US"}' ``` ### Пример запуска sync listing ```bash curl -X POST http://localhost:18000/api/v1/tasks/sync-listing \ -H "Content-Type: application/json" \ -d '{"limit": 100, "only_new": false}' ``` ## Тесты ```bash pytest -q ``` Тесты покрывают: - mapper - parser - listing - scraper - worker tasks - resilience - self-heal - db