fix car mapping
This commit is contained in:
296
README.md
Normal file
296
README.md
Normal 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
|
||||
Reference in New Issue
Block a user