diff --git a/.env b/.env new file mode 100644 index 0000000..b7bcd75 --- /dev/null +++ b/.env @@ -0,0 +1,71 @@ +IAAI_HEADLESS=false +IAAI_LOG_LEVEL=INFO +# IAAI_LOG_FILE=iaai_scraper.log + +# Network capture settings. +IAAI_CAPTURE_SAME_ORIGIN_ONLY=true +IAAI_MAX_CAPTURED_REQUESTS=40 +IAAI_MAX_CAPTURED_JSON_RESPONSES=30 + +# Sequential listing-first mode. +IAAI_CARS_LISTING_URL=https://www.iaai.com/Vehiclelisting/Cars +IAAI_MAX_PAGES_PER_RUN=10 +IAAI_MAX_VEHICLES_PER_RUN=30 +IAAI_PAGE_LINK_LIMIT=100 +IAAI_INCLUDE_PAGINATION=true +IAAI_COLLECT_CURRENT_PAGE_ONLY=false + +# Human-like pacing. +IAAI_HUMAN_PACE_ENABLED=true +IAAI_AFTER_LISTING_OPEN_MIN_S=2.5 +IAAI_AFTER_LISTING_OPEN_MAX_S=4.5 +IAAI_AFTER_FILTER_ACTION_MIN_S=2.0 +IAAI_AFTER_FILTER_ACTION_MAX_S=4.0 +IAAI_BEFORE_VEHICLE_OPEN_MIN_S=1.5 +IAAI_BEFORE_VEHICLE_OPEN_MAX_S=3.0 +IAAI_AFTER_VEHICLE_OPEN_MIN_S=1.0 +IAAI_AFTER_VEHICLE_OPEN_MAX_S=2.5 +IAAI_BETWEEN_VEHICLES_MIN_S=3.0 +IAAI_BETWEEN_VEHICLES_MAX_S=6.0 +IAAI_AFTER_PAGE_CHANGE_MIN_S=3.0 +IAAI_AFTER_PAGE_CHANGE_MAX_S=6.0 + +# Sync settings +IAAI_SYNC_ONLY_NEW=true +IAAI_TOKENS_FILE=tokens.json +IAAI_RUNTIME_CONFIG_FILE=runtime_config.json + +# Retry / backoff +IAAI_RETRY_DELAY_SECONDS=2.5 +IAAI_RETRY_BACKOFF_MULTIPLIER=2.0 +IAAI_RETRY_JITTER_SECONDS=0.25 + +# Proxy (HTTP/HTTPS preferred for Playwright) +# Chromium does not support SOCKS5 proxy authentication directly. +# Use residential or mobile USA proxy. +# IAAI_PROXY_SERVER=http://proxy.example.com:8080 +# IAAI_PROXY_USERNAME= +# IAAI_PROXY_PASSWORD= + +# Database — SQLite (локальный запуск без Docker). +# Для PostgreSQL: postgresql+psycopg2://iaai:iaai@localhost:5432/iaai_scraper +IAAI_DATABASE_URL=sqlite:///iaai_scraper.db +IAAI_DATABASE_ECHO=false +IAAI_DATABASE_POOL_SIZE=5 +IAAI_DATABASE_MAX_OVERFLOW=10 +IAAI_DATABASE_AUTO_CREATE_TABLES=true + +# Redis (Celery broker) не нужен для CLI-режима. +# Раскомментировать для запуска API + Worker + Beat. +# IAAI_REDIS_URL=redis://localhost:6379/0 + +# Celery —не нужен для CLI-режима. +# CELERY_BROKER_URL=redis://localhost:6379/0 +# CELERY_RESULT_BACKEND=redis://localhost:6379/0 +# CELERY_TASK_SOFT_TIME_LIMIT=600 +# CELERY_TASK_TIME_LIMIT=900 +# CELERY_WORKER_CONCURRENCY=1 +# CELERY_WORKER_MAX_TASKS_PER_CHILD=20 +# CELERY_BROKER_VISIBILITY_TIMEOUT=7200 +# CELERY_BEAT_SYNC_INTERVAL_MINUTES=60 +# CELERY_BEAT_SYNC_LIMIT=26 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..19c2614 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,30 @@ +name: ci + +on: + push: + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install uv + uses: astral-sh/setup-uv@v5 + + - name: Sync dependencies + run: uv sync --extra dev + + - name: Run tests + run: uv run pytest -q + + - name: Build Docker image + run: docker build -t iaai-scraper-ci . \ No newline at end of file diff --git a/.gitignore b/.gitignore index f57098e..640874d 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,6 @@ venv/ *.pyd *.log *.db -.env .vscode/ *.egg-info/ dist/ diff --git a/README.md b/README.md index dd74f65..cea617c 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,99 @@ Поэтому в проекте используются Playwright, паузы между действиями, прокси и сохранение браузерного состояния. -## Запуск +## Локальный запуск (без Docker) + +### Требования + +- **Python 3.11+** +- **Git** + +Docker **не нужен**. Данные хранятся в SQLite-файле `iaai_scraper.db` в корне проекта. + +### Быстрый старт + +```bash +git clone +cd iaai_scraper_project +pip install -e . +playwright install chromium +iaai init-db +``` + +Готовый `.env` уже в репозитории и настроен на SQLite ничего менять не нужно. + +### Команды для локального запуска и проверки + +```bash +pip install -e . +playwright install chromium +iaai init-db +iaai sync-listing --limit 10 +# С видимым браузером +iaai --headless false sync-listing --limit 5 +# Для просмотра того что записалось +python -c "import sqlite3; c=sqlite3.connect('iaai_scraper.db'); q=c.cursor(); print('cars:', q.execute('select count(*) from cars').fetchone()[0]); print('images:', q.execute('select count(*) from images').fetchone()[0]); rows=q.execute('select id, brand, model, year, origin_id from cars order by id desc limit 10').fetchall(); [print(r) for r in rows]" +``` + +### Запуск парсера + +**Скрапинг одной машины:** + +```bash +iaai sync-vehicle "https://www.iaai.com/VehicleDetail/45089484~US" +``` + +**Сбор листинга + скрапинг (например Toyota, 10 штук):** + +```bash +iaai sync-listing --make Toyota --limit 10 +``` + +**Только ссылки с листинга (без скрапинга):** + +```bash +iaai collect-listing --make Toyota +``` + +**С видимым браузером (для отладки):** + +```bash +iaai --headless false sync-vehicle "https://www.iaai.com/VehicleDetail/45089484~US" +``` + +Результаты сохраняются в `artifacts/json/` и в БД `iaai_scraper.db`. + +### Полный стек (API + Worker + Beat) + +Для API и автоматического сбора нужны **Redis** и **PostgreSQL**. В `.env` раскомментируй строки Redis/Celery и замени БД на PostgreSQL: + +```env +IAAI_DATABASE_URL=postgresql+psycopg2://iaai:iaai@localhost:5432/iaai_scraper +IAAI_REDIS_URL=redis://localhost:6379/0 +CELERY_BROKER_URL=redis://localhost:6379/0 +CELERY_RESULT_BACKEND=redis://localhost:6379/0 +``` + +Применить миграции и запустить в трёх терминалах: + +```bash +alembic upgrade head + +# Терминал 1 — API +uvicorn iaai_scraper.api.app:app --reload --port 8000 + +# Терминал 2 — Celery Worker +celery -A iaai_scraper.worker.celery_app worker --loglevel=info --concurrency=1 --pool=solo -Q scraping + +# Терминал 3 — Celery Beat (периодический запуск) +celery -A iaai_scraper.worker.celery_app beat --loglevel=info +``` + +API: `http://localhost:8000` · Swagger: `http://localhost:8000/docs` + +--- + +## Запуск через Docker (все сервисы в контейнерах) ```bash cp .env.example .env @@ -21,16 +113,14 @@ docker compose up -d Будут запущены сервисы `postgres`, `redis`, `migrate`, `api`, `worker`, `beat`. API доступен на `http://localhost:8000`. -В Docker-образ дополнительно проверяется Python-синтаксис на этапе сборки (`python -m compileall -q iaai_scraper`), чтобы не выкатывать битый код. +В Docker-образ дополнительно проверяется Python-синтаксис на этапе сборки (`python -m compileall -q iaai_scraper`), чтобы не выкатывать битый код. `entrypoint.sh` поднимает SOCKS5→HTTP proxy bridge (если задан `SOCKS5_PROXY_HOST`) и запускает `Xvfb` только для `worker` и CLI scraping-команд. -По умолчанию `beat` запускает сбор листинга **раз в 1 час** и обрабатывает **до 26 машин за запуск** (`CELERY_BEAT_SYNC_INTERVAL_MINUTES=60`, `CELERY_BEAT_SYNC_LIMIT=26`). +По умолчанию `beat` запускает сбор листинга **раз в 1 час** (`CELERY_BEAT_SYNC_INTERVAL_MINUTES=60`). Swagger-документация: `http://localhost:8000/docs` -Проверка состояния сервисов: - ```bash docker compose ps ``` diff --git a/docker-compose.yml b/docker-compose.yml index 6a6b04c..4d574a0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -40,7 +40,7 @@ services: POSTGRES_PASSWORD: iaai POSTGRES_DB: iaai_scraper ports: - - "5432:5432" + - "127.0.0.1:5432:5432" volumes: - pgdata:/var/lib/postgresql/data healthcheck: @@ -60,7 +60,7 @@ services: container_name: iaai-redis restart: unless-stopped ports: - - "6379:6379" + - "127.0.0.1:6379:6379" healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 5s @@ -94,7 +94,7 @@ services: container_name: iaai-api restart: unless-stopped ports: - - "8000:8000" + - "127.0.0.1:8000:8000" depends_on: migrate: condition: service_completed_successfully diff --git a/runtime_config.json b/runtime_config.json index a324d4d..a64bd1a 100644 --- a/runtime_config.json +++ b/runtime_config.json @@ -7,7 +7,7 @@ "condition_check_enabled": false, "lane": "iaai_cars", "only_new": true, - "limit": 50 + "limit": null }, "filters": { "price": {