# IAAI Scraper Скрапер публичного листинга автомобилей с сайта IAAI. Собирает данные карточек через Playwright, парсит HTML и перехваченные JSON ответы, нормализует и сохраняет в PostgreSQL или SQLite через SQLAlchemy. По умолчанию работает последовательно одна машина за раз, с паузами между запросами. ## Что делает 1. Открывает страницу листинга `Vehiclelisting/Cars`, собирает ссылки на карточки. 2. Переходит на каждую карточку, перехватывает XHR/fetch JSON-ответы. 3. Парсит DOM-текст, ``, встроенные `<script>` с JSON, сетевые payload'ы. 4. Маппит всё в единую структуру `CarRecord` (pydantic) с нормализацией полей. 5. Делает upsert в БД по `origin_id`, сравнивая `content_hash`, чтобы пропускать неизменившиеся записи. 6. Защищён от дублей: `origin_id` уникален, одинаковые записи пропускаются, а повторные картинки заменяются безопасно. ## Структура проекта ``` iaai_scraper/ ├── browser/ │ ├── factory.py # запуск Chrome/Chromium с desktop-фингерпринтом │ ├── network.py # перехват XHR/fetch, фильтрация и категоризация JSON │ └── pace.py # паузы между действиями ├── core/ │ ├── config.py # настройки из .env │ ├── logs.py # setup logging │ ├── retry.py # retry-декоратор │ └── utils.py # regex, deep_find_key, save_to_json ├── parsing/ │ ├── parser.py # DOM + JSON парсинг │ └── mapper.py # нормализация в CarRecord ├── storage/ │ ├── models.py # ORM: cars, images, sync_runs │ ├── schemas.py # pydantic-схемы │ ├── db.py # upsert с content_hash │ ├── listing.py # сбор ссылок из листинга │ └── enums.py # enum-значения для БД ├── scraper.py # главный модуль └── cli.py # CLI (argparse) ``` ## Установка ```bash pip install -r requirements.txt python -m playwright install chromium ``` ## Настройка Создайте `.env` на основе `.env.example`: ```env IAAI_HEADLESS=true IAAI_DATABASE_URL=postgresql+psycopg://postgres:postgres@localhost:5432/iaai_scraper ``` Все настройки (pacing, лимиты, gentle mode, retry/backoff, scheduler) задаются через переменные окружения в `.env.example`. ### БД для рабочего сайта Для рабочего запуска нужно **PostgreSQL**. Пример: ```env IAAI_DATABASE_URL=postgresql+psycopg://postgres:postgres@localhost:5432/iaai_scraper ``` SQLite подходит для локальной отладки, но не для production-сценария сайта. ### Scheduler по умолчанию Режим : - запуск раз в **1 час** - лимит **30 машин за цикл** Это уже отражено в актуальных env-настройках. ### Прокси и anti-bot IAAI использует anti-bot / fraud protection. Что важно: - предпочтительно использовать **USA residential** или **USA mobile** прокси - для Playwright лучше использовать **HTTP/HTTPS proxy** - Chromium **не поддерживает SOCKS5 с аутентификацией напрямую** - поэтому для production желательно покупать прокси, который отдаёт именно HTTP/HTTPS доступ Пример: ```env IAAI_PROXY_SERVER=http://proxy.example.com:8080 IAAI_PROXY_USERNAME=username IAAI_PROXY_PASSWORD=password ``` ### Captcha / anti-bot detection В парсере добавлены признаки для определения возможной captcha / anti-bot страницы: - `possible_captcha` - `possible_antibot` - `dom_hints.has_captcha_text` - `dom_hints.has_antibot_text` Если сайт начнёт отдавать защитную страницу, это можно увидеть в результате scrape. ## Команды ```bash # создать таблицы python main.py init-db # собрать ссылки из листинга python main.py collect-listing --make Toyota --model Camry --output listing.json # scrape одной карточки python main.py scrape-vehicle "https://www.iaai.com/VehicleDetail/41180634~US" --output result.json # scrape + запись в БД python main.py sync-vehicle "https://www.iaai.com/VehicleDetail/41180634~US" --lane iaai # массовая синхронизация листинга python main.py sync-listing --make Toyota --model Camry --lane iaai_cars --limit 30 # daemon-режим (цикл каждые N минут) python main.py run-daemon --interval 60 ``` ## Тесты ```bash pytest tests -q ``` ## Docker ```bash docker compose build docker compose run --rm iaai-scraper python main.py --help ``` Для Docker прокси также задаются через `.env`. ## Защита от дублей Система защищена от дублей на нескольких уровнях: - `origin_id` уникален в БД - при совпадении `content_hash` запись **пропускается** (`skipped`) - при обновлении запись не дублируется, а обновляется - изображения пересобираются без накопления дублей