Files
encar/README.md
2026-04-08 18:33:56 +03:00

190 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# IAAI Scraper
Скрапер листинга автомобилей с сайта IAAI.
Собирает данные карточек через Playwright, парсит HTML и перехваченные JSON ответы,
нормализует и сохраняет в PostgreSQL или SQLite через SQLAlchemy.
По умолчанию работает последовательно одна машина за раз, с паузами между запросами.
JSON-результаты CLI по умолчанию сохраняются в `artifacts/json/`, чтобы не засорять корень проекта.
## Что делает
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 машин за цикл**
- обрабатываются только **новые авто** (по умолчанию `IAAI_SYNC_ONLY_NEW=true`)
Это уже отражено в актуальных env-настройках.
### Прокси и anti-bot
IAAI использует anti-bot / fraud protection.
Что важно:
- предпочтительно использовать **USA residential** или **USA mobile** прокси
- для Playwright лучше использовать **HTTP/HTTPS proxy**
- Chromium **не поддерживает SOCKS5 с аутентификацией напрямую**
- поэтому для production желательно покупать прокси, который отдаёт именно HTTP/HTTPS доступ
Если используется встроенный bridge `iaai_scraper/proxy_bridge.py` (HTTP/HTTPS → SOCKS5),
в нём добавлены базовые меры стабильности:
- корректное чтение request body через `rfile`
- поддержка `Transfer-Encoding: chunked` для request body
- базовое логирование запросов и ошибок
- таймауты relay-соединений
- ограничение числа рабочих потоков (`PROXY_BRIDGE_MAX_WORKERS`)
- безопасный ответ `502 Bad Gateway` без утечки внутренних исключений
Пример:
```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 artifacts/json/listing.json
# scrape одной карточки
python main.py scrape-vehicle "https://www.iaai.com/VehicleDetail/41180634~US" --output artifacts/json/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
# при необходимости можно принудительно отключить фильтр only-new
python main.py sync-listing --limit 30 --only-new false
# daemon-режим (цикл каждые N минут)
python main.py run-daemon --interval 60
```
## Тесты
```bash
pytest tests -q
```
## Docker
```bash
# собрать образ
docker compose build
# запустить daemon (по умолчанию run-daemon)
docker compose up -d
# посмотреть логи
docker compose logs -f
# одноразовая команда
docker compose run --rm iaai-scraper python main.py sync-listing --limit 5
# остановить (graceful shutdown)
docker compose down
```
Контейнер автоматически перезапускается при крашах (`restart: unless-stopped`).
Для Docker прокси также задаются через `.env`.
## Защита от дублей
Система защищена от дублей на нескольких уровнях:
- `origin_id` уникален в БД
- при совпадении `content_hash` запись **пропускается** (`skipped`)
- при обновлении запись не дублируется, а обновляется
- изображения пересобираются без накопления дублей