161 lines
6.0 KiB
Markdown
161 lines
6.0 KiB
Markdown
# IAAI Scraper
|
||
|
||
Скрапер листинга автомобилей с сайта IAAI.
|
||
Собирает данные карточек через Playwright, парсит HTML и перехваченные JSON ответы,
|
||
нормализует и сохраняет в PostgreSQL или SQLite через SQLAlchemy.
|
||
|
||
По умолчанию работает последовательно одна машина за раз, с паузами между запросами.
|
||
|
||
## Что делает
|
||
|
||
1. Открывает страницу листинга `Vehiclelisting/Cars`, собирает ссылки на карточки.
|
||
2. Переходит на каждую карточку, перехватывает XHR/fetch JSON-ответы.
|
||
3. Парсит DOM-текст, `<title>`, встроенные `<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`)
|
||
- при обновлении запись не дублируется, а обновляется
|
||
- изображения пересобираются без накопления дублей
|
||
|
||
|
||
|