add project setup
This commit is contained in:
152
README.md
Normal file
152
README.md
Normal file
@@ -0,0 +1,152 @@
|
||||
# OpenLane Scraper
|
||||
|
||||
Парсер автомобилей с [OpenLane marketplace](https://app.openlane.com). Забирает данные через API (`GET /api/v4/search`), сохраняет в PostgreSQL. Работает через Playwright (для авторизации), FastAPI, Celery и Docker.
|
||||
|
||||
## Архитектура
|
||||
|
||||
- **Авторизация**: Okta OAuth2 PKCE через `okta.iam.karglobal.com`. Сессия сохраняется в `storage_state.json` и переиспользуется между запусками.
|
||||
- **Сбор данных**: API-запросы с заголовками `x-rbz-user-id`, `x-rbz-dealership-id` (извлекаются из JWT access_token).
|
||||
- **Защита аккаунта**: throttle 2–5s между запросами, jitter 0–120s перед стартом, circuit breaker (2 ошибки подряд → стоп), 403→немедленный стоп, 429→backoff×3.
|
||||
- **Anti-detection**: блокировка ресурсов (bugsnag, intercom, pusher, analytics), stealth patches.
|
||||
|
||||
## Локальный запуск
|
||||
|
||||
### Требования
|
||||
|
||||
- **Python 3.11+**
|
||||
- **PostgreSQL** и **Redis** (для Celery)
|
||||
|
||||
### Быстрый старт
|
||||
|
||||
```bash
|
||||
git clone <repo-url>
|
||||
cd openlane_scraper_project
|
||||
pip install -e .
|
||||
playwright install chromium
|
||||
|
||||
cp .env.example .env
|
||||
# Заполнить OPENLANE_USERNAME, OPENLANE_PASSWORD в .env
|
||||
|
||||
alembic upgrade head
|
||||
```
|
||||
|
||||
### Запуск
|
||||
|
||||
```bash
|
||||
# Терминал 1 — API
|
||||
uvicorn openlane_scraper.api.app:app --reload --port 8000
|
||||
|
||||
# Терминал 2 — Celery Worker
|
||||
celery -A openlane_scraper.worker.celery_app worker --loglevel=info --concurrency=1 --pool=solo -Q scraping
|
||||
|
||||
# Терминал 3 — Celery Beat (каждый час)
|
||||
celery -A openlane_scraper.worker.celery_app beat --loglevel=info
|
||||
```
|
||||
|
||||
CLI:
|
||||
|
||||
```bash
|
||||
openlane scrape-openlane --limit 20
|
||||
```
|
||||
|
||||
## Docker
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Сервисы: `postgres`, `redis`, `migrate`, `api`, `worker`, `beat`.
|
||||
|
||||
|
||||
- `beat` запускает сбор **раз в 1 час** (`CELERY_BEAT_SYNC_INTERVAL_MINUTES=60`)
|
||||
- Лимит по умолчанию: 20 машин (`OPENLANE_MAX_CARS_LIMIT=20`)
|
||||
|
||||
API (Docker): `http://localhost:58000` · Swagger: `http://localhost:58000/docs`
|
||||
|
||||
### Первый логин (storage state)
|
||||
|
||||
```bash
|
||||
docker compose run --rm --service-ports openlane-auth openlane openlane-login
|
||||
# Войти вручную в браузере — storage state сохранится в volume
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
- `GET /health` — статус сервиса
|
||||
- `GET /api/v1/stats` — статистика (машины, картинки, топ брендов)
|
||||
- `GET /api/v1/cars` — список с пагинацией
|
||||
- `GET /api/v1/cars/{id}` — карточка с картинками
|
||||
- `POST /api/v1/tasks/sync-listing` — запустить сбор
|
||||
- `GET /api/v1/sync-runs` — история запусков
|
||||
|
||||
## Структура
|
||||
|
||||
```text
|
||||
openlane_scraper/
|
||||
scraper.py — оркестратор: circuit breaker, batched upsert, early-stop
|
||||
cli.py — CLI-команды (scrape-openlane, openlane-login)
|
||||
|
||||
browser/
|
||||
factory.py — создание браузера, stealth-инъекции, resource blocking
|
||||
|
||||
openlane/
|
||||
auth.py — авторизация через storage_state / Okta PKCE
|
||||
client.py — запросы к /api/v4/search с JWT-заголовками
|
||||
checkpoint.py — checkpoint/resume по страницам
|
||||
writer.py — JSONL + aggregated JSON
|
||||
runner.py — orchestration, retry/backoff, progress
|
||||
|
||||
storage/
|
||||
models.py — SQLAlchemy: Car, Image, SyncRun
|
||||
schemas.py — Pydantic: CarRecord, ImageRecord, CarRead
|
||||
enums.py — enum-значения (drive, gearbox, body_type, ...)
|
||||
db.py — PersistenceService: upsert, sync runs, статистика
|
||||
|
||||
api/
|
||||
app.py — FastAPI factory
|
||||
deps.py — dependency injection
|
||||
routes/ — health, cars, tasks
|
||||
|
||||
worker/
|
||||
celery_app.py — конфиг Celery, beat-расписание
|
||||
tasks.py — sync_listing_task с jitter и lock
|
||||
|
||||
core/
|
||||
config.py — Settings (dataclass), env-переменные
|
||||
logs.py — логирование с trace_id
|
||||
retry.py — @retryable с exponential backoff
|
||||
runtime_config.py — runtime-конфиг
|
||||
utils.py — утилиты
|
||||
```
|
||||
|
||||
## Конфигурация
|
||||
|
||||
Всё через env-переменные (полный список в `.env.example`):
|
||||
|
||||
- **OpenLane**: `OPENLANE_USERNAME`, `OPENLANE_PASSWORD`, `OPENLANE_MAX_CARS_LIMIT`
|
||||
- **БД**: `OPENLANE_DATABASE_URL`, `OPENLANE_DATABASE_POOL_SIZE`
|
||||
- **Redis**: `OPENLANE_REDIS_URL`
|
||||
- **Celery**: `CELERY_BROKER_URL`, `CELERY_BEAT_SYNC_INTERVAL_MINUTES`
|
||||
- **Прокси**: `OPENLANE_PROXY_SERVER`, `OPENLANE_PROXY_USERNAME`, `OPENLANE_PROXY_PASSWORD`
|
||||
|
||||
## Миграции
|
||||
|
||||
В Docker миграции выполняются сервисом `migrate` (`alembic upgrade head`).
|
||||
|
||||
```bash
|
||||
alembic upgrade head
|
||||
alembic revision --autogenerate -m "add_column_x"
|
||||
```
|
||||
|
||||
## Тесты
|
||||
|
||||
```bash
|
||||
pytest -q
|
||||
```
|
||||
|
||||
Тесты работают на SQLite in-memory, без внешних зависимостей.
|
||||
|
||||
## Runtime-фильтры
|
||||
|
||||
Файл `runtime_config.json` управляет runtime-поведением sync и фильтрацией автомобилей.
|
||||
Reference in New Issue
Block a user