fix car mapping

This commit is contained in:
qananasikq
2026-07-01 13:49:30 +03:00
commit 3cd7e785da
68 changed files with 14973 additions and 0 deletions

296
README.md Normal file
View File

@@ -0,0 +1,296 @@
# 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