# IAAI Scraper Парсер аукционных автомобилей с [iaai.com](https://www.iaai.com). Ходит по листингу, собирает карточки машин, вытаскивает данные из DOM и перехваченных XHR-ответов, складывает всё в PostgreSQL. Работает через Playwright (headless Chromium), крутится в Docker. ## Как устроен сайт и его защита У IAAI стоит **Imperva Incapsula** — внешний WAF и anti-bot. - **Anti-bot** — headless Chromium без прокси часто режется. - **Динамическая подгрузка** — часть данных приходит через XHR (`/Search`, `/VehicleDetail`), часть есть в HTML. - **Cookie consent** — при первом заходе показывают баннер. Поэтому в проекте используется Playwright, паузы между действиями и прокси. ## Запуск ```bash cp .env.example .env # подправить под себя docker compose up -d ``` Поднимутся 5 контейнеров: postgres, redis, api, worker, beat. API на `http://localhost:8000`. По умолчанию `beat` запускает сбор листинга **раз в 1 час** и обрабатывает **до 26 машин за запуск** (`CELERY_BEAT_SYNC_INTERVAL_MINUTES=60`, `CELERY_BEAT_SYNC_LIMIT=26`). Swagger-документация: `http://localhost:8000/docs` ## API **Здоровье и статистика:** - `GET /health` — статус сервиса и подключения к БД - `GET /api/v1/stats` — сколько машин/картинок в базе, топ брендов **Машины:** - `GET /api/v1/cars` — список с пагинацией - `GET /api/v1/cars/{id}` — карточка с картинками - `GET /api/v1/cars/by-origin/{origin_id}` — поиск по IAAI stock number **Задачи:** - `POST /api/v1/tasks/sync-vehicle` — скрапнуть одну машину по URL - `POST /api/v1/tasks/sync-listing` — запустить полный обход листинга - `GET /api/v1/tasks/{task_id}` — статус задачи - `GET /api/v1/tasks` — все задачи - `GET /api/v1/sync-runs` — история запусков Пример — скрапнуть конкретную машину: ```bash curl -X POST http://localhost:8000/api/v1/tasks/sync-vehicle -H "Content-Type: application/json" \ -d '{"vehicle_url": "https://www.iaai.com/VehicleDetail/45089484~US"}' ``` ## CLI Для отладки без API/Celery: ```bash python main.py init-db python main.py collect-listing --make Toyota python main.py sync-vehicle "https://www.iaai.com/VehicleDetail/45089484~US" python main.py sync-listing --limit 10 ``` ## Структура ``` iaai_scraper/ scraper.py — оркестратор: связывает browser → parser → storage cli.py — CLI-команды (init-db, sync-vehicle, sync-listing, ...) proxy_bridge.py — HTTP→SOCKS5 мост (Chromium не умеет SOCKS5 с авторизацией) browser/ factory.py — создание браузера, stealth-инъекции, fingerprint listing.py — сбор ссылок на машины с листинга, пагинация network.py — перехват XHR/fetch ответов через Playwright events pace.py — рандомные паузы и движение мыши parsing/ parser.py — VehicleParser: 3 канала (DOM + XHR JSON + embedded JSON) mapper.py — CarMapper: нормализация → CarRecord, content hash storage/ models.py — SQLAlchemy: Car (30+ полей), Image, SyncRun, ScrapeTask schemas.py — Pydantic: CarRecord, ImageRecord, CarRead enums.py — допустимые значения (drive, gearbox, body_type, ...) db.py — PersistenceService: upsert (insert/update/skip), sync runs api/ app.py — FastAPI factory, lifespan, роутеры deps.py — dependency injection (Settings, PersistenceService) routes/ health.py — GET /health cars.py — CRUD по машинам + GET /stats tasks.py — управление Celery-задачами + sync-runs worker/ celery_app.py — конфиг Celery, beat-расписание tasks.py — sync_vehicle_task, sync_listing_task core/ config.py — Settings (dataclass), все env-переменные logs.py — логирование с trace_id (ContextVar) retry.py — декоратор @retryable с exponential backoff utils.py — VIN_RE, deep_find_key, вспомогательные функции alembic/ — миграции БД tests/ — 30 тестов (SQLite in-memory) ``` ## Конфигурация Всё через env-переменные (полный список в `.env.example`): **БД:** `IAAI_DATABASE_URL`, `IAAI_DATABASE_POOL_SIZE` **Redis:** `IAAI_REDIS_URL` **Celery:** `CELERY_BROKER_URL`, `CELERY_BEAT_SYNC_INTERVAL_MINUTES` **Скрапер:** `IAAI_HEADLESS`, `IAAI_SYNC_ONLY_NEW`, `IAAI_MAX_PAGES_PER_RUN` **Прокси:** `IAAI_PROXY_SERVER`, `IAAI_PROXY_USERNAME`, `IAAI_PROXY_PASSWORD` **Паузы:** `IAAI_BETWEEN_VEHICLES_MIN_S`, `IAAI_AFTER_PAGE_CHANGE_MAX_S` и т.д. ## Миграции В Docker миграции накатываются автоматически при старте API-контейнера (`alembic upgrade head` в entrypoint). Вручную: ```bash alembic upgrade head alembic revision --autogenerate -m "add_column_x" ``` ## Тесты ```bash pytest -q ``` 30 тестов, SQLite in-memory, без внешних зависимостей.