Files
supermarket/.cursor/plans/supermarket_scraper_system_1af4ed29.plan.md

39 KiB
Raw Blame History

name, overview, todos
name overview todos
Supermarket Scraper System Создание комплексной системы скрапинга товаров из магазинов Магнит и Пятёрочка с веб-скрапингом (Playwright), автоматизацией Android-приложений (Appium), хранением в PostgreSQL с pgvector для embeddings, интеграцией LangChain для LLM-запросов, и планировщиком для автоматического обновления данных.

План разработки системы скрапинга товаров супермаркетов

Архитектура системы

┌─────────────────────────────────────────────────────────────┐
                     Data Sources                             
├──────────┬──────────────┬───────────────────────────────────┤
   API      Web Scrapers    Android App Scrapers           
 Scrapers  (Playwright)          (Appium)                  
                                                            
 - Magnit  - magnit.ru   - Магнит Android App              
   API     - 5ka.ru      - 5ka Android App                 
                                                            
 POST      Fallback/     Для доставки и                    
 /webgate  Дополнение    позиций доставки                  
└────┬─────┴──────┬───────┴──────────┬────────────────────────┘
                                   
     └────────────┴──────────────────┘
                  
     ┌────────────▼────────────┐
        Data Processing Layer 
       - Parsing & Normalize  
       - Embeddings Generation
     └────────────┬────────────┘
                  
     ┌────────────▼────────────┐
        PostgreSQL Database   
       - pgvector extension   
       - Products, Categories 
       - Price History        
       - Stores               
     └────────────┬────────────┘
                  
     ┌────────────▼────────────┐
        LangChain + LLM       
       - SQL Agent            
       - Query Interface      
     └─────────────────────────┘

Структура проекта

supermarket/
├── package.json
├── tsconfig.json
├── .env.example
├── docker-compose.yml              # PostgreSQL с pgvector
├── src/
   ├── config/
      ├── database.ts            # DB connection & setup
      ├── appium.ts              # Appium config
      └── llm.ts                 # LangChain/LLM config
   
   ├── database/
      ├── prisma/
         ├── schema.prisma      # Prisma schema
         └── migrations/        # Автоматические миграции Prisma
      ├── client.ts              # Prisma Client instance
      └── seeders/               # Initial data (Prisma seed)
   
   ├── scrapers/
      ├── api/
         ├── base/
            └── BaseApiScraper.ts
         ├── magnit/
            ├── MagnitApiScraper.ts
            ├── types.ts
            └── endpoints.ts      # API endpoints и параметры
         └── 5ka/
             ├── 5kaApiScraper.ts
             ├── types.ts
             └── endpoints.ts
      
      ├── web/
         ├── base/
            └── BaseWebScraper.ts
         ├── magnit/
            ├── MagnitWebScraper.ts
            ├── types.ts
            └── selectors.ts
         └── 5ka/
             ├── 5kaWebScraper.ts
             ├── types.ts
             └── selectors.ts
      
      └── android/
          ├── base/
             └── BaseAppScraper.ts
          ├── magnit/
             ├── MagnitAppScraper.ts
             └── selectors.ts
          └── 5ka/
              ├── 5kaAppScraper.ts
              └── selectors.ts
   
   ├── services/
      ├── auth/
         ├── WebAuthService.ts
         ├── AppAuthService.ts
         └── SessionManager.ts   # Управление сессиями для API
      ├── embeddings/
         └── EmbeddingService.ts    # Генерация embeddings для товаров
      ├── parser/
         └── ProductParser.ts       # Парсинг и нормализация данных
      └── scheduler/
          └── SchedulerService.ts    # Cron-like планировщик
   
   ├── api/
      ├── server.ts                 # HTTP сервер (Express/Fastify)
      ├── routes/
         ├── index.ts             # Главный роутер
         ├── scrapers.ts          # Endpoints для скрапинга
         ├── embeddings.ts        # Endpoints для embeddings
         ├── scheduler.ts         # Endpoints для планировщика
         └── health.ts            # Health check endpoints
      ├── controllers/
         ├── ScraperController.ts
         ├── EmbeddingController.ts
         └── SchedulerController.ts
      ├── middlewares/
         ├── auth.ts              # Аутентификация (опционально)
         ├── errorHandler.ts
         └── requestLogger.ts
      └── types/
          └── requests.ts          # Типы для API запросов
   
   ├── llm/
      ├── agents/
         └── SQLAgent.ts           # LangChain SQL Agent
      ├── chains/
         └── QueryChain.ts         # Цепочки запросов
      └── prompts/
          └── prompts.ts            # Промпты для LLM
   
   ├── scripts/
      ├── scrape-api.ts            # Ручной запуск API-скрапинга
      ├── scrape-web.ts            # Ручной запуск веб-скрапинга (fallback)
      ├── scrape-android.ts        # Ручной запуск Android-скрапинга
      ├── generate-embeddings.ts   # Генерация embeddings
      ├── migrate.ts               # Запуск Prisma миграций (prisma migrate)
      └── start-server.ts          # Запуск API сервера
   
   └── utils/
       ├── logger.ts
       └── errors.ts

└── tests/
    ├── scrapers/
    └── services/

База данных (PostgreSQL + pgvector + Prisma)

Prisma Schema - Основные модели:

model Store {
  id          Int      @id @default(autoincrement())
  name        String
  type        String   // "web" | "app"
  code        String?  // storeCode для API (например "992301")
  url         String?
  region      String?
  address     String?
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt
  
  products    Product[]
  sessions    ScrapingSession[]
}

model Category {
  id          Int      @id @default(autoincrement())
  externalId  Int?     // ID из внешнего API
  name        String
  parentId    Int?
  parent      Category? @relation("CategoryHierarchy", fields: [parentId], references: [id])
  children    Category[] @relation("CategoryHierarchy")
  description String?
  embedding   Unsupported("vector(1536)")? // pgvector (добавится в Этапе 3)
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt
  
  products    Product[]
}

model Product {
  id            Int      @id @default(autoincrement())
  externalId    String   // ID из API (например "1000300796")
  storeId       Int
  categoryId    Int?
  name          String
  description   String?
  url           String?
  imageUrl      String?
  currentPrice  Decimal  @db.Decimal(10, 2)
  unit          String?  // единица измерения
  weight        String?  // вес/объем
  brand         String?
  embedding     Unsupported("vector(1536)")? // pgvector (добавится в Этапе 3)
  createdAt     DateTime @default(now())
  updatedAt     DateTime @updatedAt
  
  store         Store    @relation(fields: [storeId], references: [id])
  category      Category? @relation(fields: [categoryId], references: [id])
  priceHistory  PriceHistory[]
  
  @@unique([externalId, storeId])
  @@index([storeId])
  @@index([categoryId])
}

model PriceHistory {
  id          Int      @id @default(autoincrement())
  productId   Int
  price       Decimal  @db.Decimal(10, 2)
  oldPrice    Decimal? @db.Decimal(10, 2) // старая цена при акции
  source      String   // "api" | "web" | "app"
  createdAt   DateTime @default(now())
  
  product     Product  @relation(fields: [productId], references: [id])
  
  @@index([productId])
  @@index([createdAt])
}

model ScrapingSession {
  id          Int      @id @default(autoincrement())
  storeId     Int
  sourceType  String   // "api" | "web" | "app"
  status      String   // "pending" | "running" | "completed" | "failed"
  startedAt   DateTime @default(now())
  finishedAt  DateTime?
  error       String?
  
  store       Store    @relation(fields: [storeId], references: [id])
  
  @@index([storeId])
  @@index([status])
  @@index([startedAt])
}

Примечания:

  • Unsupported("vector(1536)") - используется для pgvector, Prisma не имеет нативной поддержки
  • Для работы с pgvector используем Prisma Raw queries
  • Embeddings добавляются в Этапе 3 (после настройки базовой схемы)

Этапы реализации (приоритеты разработки)

Этап 1: База данных и Prisma (MVP - начало)

Цель: Настроить БД для сохранения товаров

  1. Инициализация TypeScript проекта
  2. Настройка PostgreSQL с pgvector (Docker)
  3. Установка и настройка Prisma ORM
  4. Создание Prisma schema:
  • Модель Store (магазины)
  • Модель Category (категории)
  • Модель Product (товары) - базовые поля без embeddings
  • Модель PriceHistory (история цен)
  • Модель ScrapingSession (сессии скрапинга)
  1. Создание миграций Prisma
  2. Настройка Prisma Client и подключения к БД
  3. Тестирование подключения и базовых операций

Этап 2: API скрапинг Магнита и сохранение в БД (MVP - продолжение)

Цель: Получить данные через API Магнита и сохранить в БД

  1. Настройка базовых зависимостей (playwright, axios, dotenv)
  2. Реализация MagnitApiScraper с обходом защиты (Playwright для сессии)
  • Метод инициализации сессии через Playwright
  • Метод поиска товаров (POST /webgate/v2/goods/search)
  • Получение 2-3 страниц товаров для тестирования
  • Парсинг и нормализация данных из API ответа
  1. Интеграция MagnitApiScraper с БД через Prisma
  2. Сохранение товаров в БД через Prisma Client
  3. Сохранение истории цен при каждом скрапинге
  4. Тестирование на нескольких категориях товаров
  5. Проверка сохранения данных в PostgreSQL

Этап 3: Embeddings и векторный поиск

Цель: Добавить embeddings для товаров и категорий

  1. Установка и настройка pgvector расширения в PostgreSQL
  2. Обновление Prisma schema:
  • Добавление поля embedding vector(1536) в модель Product
  • Добавление поля embedding vector(1536) в модель Category
  1. Создание миграции для pgvector
  2. Интеграция с моделью для embeddings (OpenAI embeddings или Ollama)
  3. Реализация EmbeddingService:
  • Генерация embeddings для названий и описаний товаров
  • Батчовая обработка для эффективности
  1. Обновление индексов pgvector для быстрого поиска
  2. Функции векторного поиска в PostgreSQL через Prisma Raw queries

Этап 4: API-скрапинг - полная реализация

  1. Базовый класс BaseApiScraper с общим функционалом
  • HTTP клиент (axios) с настройкой заголовков
  • Retry logic и обработка ошибок
  • Rate limiting
  • Интеграция с Playwright для получения сессий
  1. MagnitApiScraper для API magnit.ru/webgate/v2/goods/search
  • Инициализация через Playwright (получение cookies и device-id)
  • Метод initialize() - запуск браузера, получение сессии
  • Метод searchGoods() - POST запрос для поиска товаров
  • Метод getProductStores() - GET запрос для информации о товаре в магазине
  • Обработка 403 Forbidden (автоматическое обновление сессии)
  • Получение списка категорий
  • Пагинация товаров по категориям
  • Обработка ответа API (items, prices, promotions, ratings)
  • Маппинг API ответов в единый формат
  • Управление жизненным циклом браузера (переиспользование сессии)
  1. 5kaApiScraper (если доступен API, аналогичная структура)
  2. Сервис парсинга и нормализации товаров
  3. Сохранение данных в БД

Этап 5: Расширение функционала (после MVP)

Этап 5.1: 5ka API скрапинг

  1. Реализация 5kaApiScraper (если доступен API)
  2. Интеграция с БД через Prisma

Этап 5.2: Веб-скрапинг (Playwright) - fallback/дополнение

  1. Базовый класс BaseWebScraper с общим функционалом
  2. MagnitWebScraper с авторизацией (если API недоступен)
  3. 5kaWebScraper с авторизацией
  4. Интеграция как резервный метод

Этап 5.3: Android-скрапинг (Appium)

  1. Настройка Appium и WebDriverIO
  2. Базовый класс BaseAppScraper
  3. MagnitAppScraper для Android приложения
  4. 5kaAppScraper для Android приложения
  5. Сервис авторизации в приложениях

Этап 6: LangChain интеграция

  1. Настройка LangChain SQL Agent
  2. Подключение к PostgreSQL через Prisma
  3. Промпты для работы с товарами и ценами
  4. Реализация запросов типа "самый дешевый попкорн"

Этап 7: Планировщик и автоматизация

  1. SchedulerService с cron-like функционалом
  • node-cron для расписаний
  • Управление задачами (запуск, остановка, статус)
  • Конфигурация расписаний через конфиг-файл
  1. REST API Server для внешних интеграций
  • HTTP сервер (Express или Fastify)
  • API endpoints для триггеров из n8n и других сервисов
  • Документация API (OpenAPI/Swagger)
  1. API Endpoints:
  • POST /api/scrapers/api/magnit - Запуск API-скрапинга Магнита
  • POST /api/scrapers/api/5ka - Запуск API-скрапинга 5ka
  • POST /api/scrapers/web/magnit - Запуск веб-скрапинга Магнита
  • POST /api/scrapers/web/5ka - Запуск веб-скрапинга 5ka
  • POST /api/scrapers/android/magnit - Запуск Android-скрапинга Магнита
  • POST /api/scrapers/android/5ka - Запуск Android-скрапинга 5ka
  • POST /api/embeddings/generate - Генерация embeddings для товаров
  • POST /api/scheduler/jobs - Создание задачи в планировщике
  • GET /api/scheduler/jobs - Список задач планировщика
  • GET /api/scheduler/jobs/:id - Статус конкретной задачи
  • DELETE /api/scheduler/jobs/:id - Удаление задачи
  • POST /api/scheduler/jobs/:id/trigger - Ручной запуск задачи
  • GET /api/health - Health check
  • GET /api/stats - Статистика скрапинга
  1. Конфигурация расписаний обновления
  2. Логирование и обработка ошибок
  3. CLI для ручного запуска скриптов (как альтернатива API)

Этап 8: Обработка истории цен

  1. Автоматическое создание записей в price_history (уже в Этапе 2)
  2. Анализ изменений цен
  3. Уведомления о значительных изменениях (опционально)

Технологический стек

  • Runtime: Node.js + TypeScript
  • API Scraping: Axios/Fetch для HTTP запросов (приоритетный метод)
  • Web Scraping: Playwright (fallback метод)
  • Mobile Automation: Appium + WebDriverIO
  • Database: PostgreSQL 15+ с расширением pgvector
  • ORM: Prisma
  • LLM Integration: LangChain + OpenAI/Ollama
  • Scheduler: node-cron
  • HTTP Server: Express или Fastify для REST API
  • API Documentation: OpenAPI/Swagger
  • Docker: для PostgreSQL с pgvector

Ключевые зависимости

{
  "dependencies": {
    "axios": "^1.6.2",
    "playwright": "^1.40.0",
    "webdriverio": "^8.25.0",
    "@wdio/appium-service": "^8.25.0",
    "pg": "^8.11.0",
    "pgvector": "^0.1.8",
    "typeorm": "^0.3.17",
    "langchain": "^0.1.0",
    "@langchain/openai": "^0.0.5",
    "langchain-sql": "^0.1.0",
    "openai": "^4.20.0",
    "node-cron": "^3.0.3",
    "dotenv": "^16.3.1",
    "express": "^4.18.2",
    "cors": "^2.8.5",
    "helmet": "^7.1.0",
    "swagger-ui-express": "^5.0.0",
    "swagger-jsdoc": "^6.2.8"
  },
  "devDependencies": {
    "@types/node": "^20.10.0",
    "@types/node-cron": "^3.0.11",
    "@types/express": "^4.17.21",
    "@types/cors": "^2.8.17",
    "@types/swagger-ui-express": "^4.1.6",
    "@types/swagger-jsdoc": "^6.0.4",
    "typescript": "^5.3.0",
    "ts-node": "^10.9.0"
  }
}

API Магнита - Структура данных и обход защиты

Основной Endpoint: POST https://magnit.ru/webgate/v2/goods/search

Request Body:

{
  sort: { order: "desc" | "asc", type: "popularity" | "price" | "name" },
  pagination: { limit: number, offset: number },
  categories: number[],           // ID категорий
  includeAdultGoods: boolean,
  storeCode: string,              // Код магазина (например "992301")
  storeType: string,              // Тип магазина (например "6")
  catalogType: string             // Тип каталога (например "1")
}

Дополнительный Endpoint: GET https://magnit.ru/webgate/v2/goods/{productId}/stores/{storeCode}

Используется для получения информации о товаре в конкретном магазине:

  • URL: /webgate/v2/goods/1000257489/stores/992301?storetype=6&catalogtype=1
  • Метод: GET
  • Проблема: Возвращает 403 Forbidden без правильных заголовков и cookies

Response Structure (search endpoint):

{
  category: { id: number, title: string, fullMatch: boolean },
  items: Array<{
    id: string,
    productId: string,
    name: string,
    price: number,                // Цена в копейках (24999 = 249.99 руб)
    promotion?: {
      isPromotion: boolean,
      oldPrice?: number,
      discountPercent?: number,
      endDate: string
    },
    gallery: Array<{ url: string, type: string }>,
    ratings: {
      rating: number,
      scoresCount: number,
      commentsCount: number
    },
    quantity: number,             // Остаток на складе
    badges: Array<{ text: string, backgroundColor: string }>,
    storeCode: string,
    // ... другие поля
  }>,
  fastCategoriesExtended: Array<{ id: number, title: string }>
}

Проблема 403 Forbidden и обход защиты

API Магнита защищен и требует:

  1. Обязательные заголовки:
  • x-device-id - Device ID из cookie mg_udi (формат UUID: 9D4909B8-9F79-5B1D-8457-EA519EC9AA3F)
  • x-client-name: magnit
  • x-device-platform: Web
  • x-app-version: 2025.12.12-16.56 (актуальная версия)
  • x-new-magnit: true
  • x-platform-version: Windows Chrome 130
  • referer: https://magnit.ru/
  • user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...
  1. Обязательные cookies:
  • shopCode=992301
  • mg_udi - Device ID (UUID формат)
  • oxxfgh, uwyii, uwyiert - сессионные cookies
  • nmg_dt, nmg_sp, mg_uac, mg_adlt - другие служебные cookies
  1. Заголовки браузера:
  • accept: */*
  • accept-language: ru,en-US;q=0.9,en;q=0.8
  • accept-encoding: gzip, deflate, br, zstd
  • sec-ch-ua, sec-fetch-dest, sec-fetch-mode, sec-fetch-site

Решение: Гибридный подход (Playwright + Axios)

Вариант 1: Playwright для инициализации сессии, затем Axios для запросов

// Псевдокод реализации
class MagnitApiScraper {
  // 1. Инициализация через Playwright
  async initialize() {
    // Запуск браузера
    browser = await chromium.launch({ headless: true });
    page = await browser.newPage();
    
    // Переход на главную для получения cookies
    await page.goto('https://magnit.ru/', { waitUntil: 'networkidle' });
    
    // Извлечение cookies
    cookies = await page.context().cookies();
    deviceId = cookies.find(c => c.name === 'mg_udi')?.value;
    
    // Настройка axios с cookies и заголовками
    httpClient.defaults.headers.cookie = formatCookies(cookies);
    httpClient.defaults.headers['x-device-id'] = deviceId;
    // ... остальные заголовки
  }
  
  // 2. API запросы через axios
  async getProductStores(productId: string, storeCode: string) {
    // Автоматическое обновление сессии при 403
    if (response.status === 403) {
      await this.initialize();
      return this.getProductStores(productId, storeCode);
    }
  }
}

Вариант 2: Использование Playwright Request API (рекомендуется)

// Использование встроенного request API Playwright
async getProductStores(productId: string, storeCode: string) {
  // Playwright автоматически передает cookies и контекст браузера
  const response = await page.request.get(
    `https://magnit.ru/webgate/v2/goods/${productId}/stores/${storeCode}`,
    {
      params: { storetype: '6', catalogtype: '1' },
      headers: {
        'accept': '*/*',
        'referer': 'https://magnit.ru/',
        'x-app-version': '2025.12.12-16.56',
        'x-client-name': 'magnit',
        'x-device-platform': 'Web',
        'x-new-magnit': 'true',
      }
    }
  );
  return response.json();
}

Структура реализации в проекте

// src/scrapers/api/magnit/MagnitApiScraper.ts
export class MagnitApiScraper extends BaseApiScraper {
  private browser: Browser | null = null;
  private page: Page | null = null;
  private deviceId: string = '';
  
  async initialize() {
    // Запуск Playwright, получение cookies и device-id
    // Сохранение сессии для переиспользования
  }
  
  async searchGoods(params: SearchParams) {
    // POST /webgate/v2/goods/search
    // Использование page.request.post() или axios с cookies
  }
  
  async getProductStores(productId: string, storeCode: string) {
    // GET /webgate/v2/goods/{productId}/stores/{storeCode}
    // Обработка 403, автоматическое обновление сессии
  }
  
  async close() {
    // Закрытие браузера
  }
}

Важные моменты реализации

  • Инициализация сессии: Один раз при старте скрапера, переиспользование cookies
  • Обновление сессии: Автоматическое обновление при получении 403
  • Управление браузером: Переиспользование одного браузера для всех запросов
  • Rate limiting: Добавление задержек между запросами (200-500ms)
  • Обработка ошибок: Retry logic с экспоненциальной задержкой
  • Кэширование: Сохранение device-id и cookies в файл для переиспользования между запусками

Важные моменты:

  • Цены в API хранятся в копейках (24999 = 249.99 руб)
  • Нужно получать список категорий отдельным запросом или парсить с сайта
  • Пагинация через offset и limit
  • Для каждого магазина нужен свой storeCode
  • Обязательно использовать Playwright для получения cookies перед API запросами

REST API Endpoints для внешних интеграций

База URL: http://localhost:3000/api (или переменная окружения API_PORT)

Endpoints для скрапинга

POST /api/scrapers/api/magnit

Запуск API-скрапинга МагнитаRequest Body:

{
  storeCode?: string;        // Код магазина (по умолчанию из конфига)
  categories?: number[];     // ID категорий для скрапинга
  pagination?: {             // Параметры пагинации
    limit: number;
    offset: number;
  };
}

Response:

{
  jobId: string;             // ID задачи
  status: "started" | "running" | "completed" | "failed";
  startedAt: string;
}

POST /api/scrapers/api/5ka

Запуск API-скрапинга 5ka (аналогичная структура)

POST /api/scrapers/web/magnit

Запуск веб-скрапинга Магнита через Playwright

POST /api/scrapers/web/5ka

Запуск веб-скрапинга 5ka через Playwright

POST /api/scrapers/android/magnit

Запуск Android-скрапинга Магнита через Appium

POST /api/scrapers/android/5ka

Запуск Android-скрапинга 5ka через Appium

Endpoints для embeddings

POST /api/embeddings/generate

Генерация embeddings для товаровRequest Body:

{
  productIds?: number[];     // Конкретные товары (если не указано - все)
  batchSize?: number;        // Размер батча (по умолчанию 100)
  force?: boolean;           // Перегенерировать существующие
}

Response:

{
  jobId: string;
  totalProducts: number;
  processed: number;
  status: string;
}

Endpoints для планировщика

POST /api/scheduler/jobs

Создание задачи в планировщикеRequest Body:

{
  name: string;              // Название задачи
  schedule: string;          // Cron выражение (например "0 0 * * *")
  taskType: "api_scrape" | "web_scrape" | "android_scrape" | "generate_embeddings";
  taskConfig: {              // Конфигурация задачи
    store: "magnit" | "5ka";
    categories?: number[];
    // ... другие параметры
  };
  enabled?: boolean;         // Включена ли задача (по умолчанию true)
}

GET /api/scheduler/jobs

Список всех задач планировщикаResponse:

{
  jobs: Array<{
    id: string;
    name: string;
    schedule: string;
    taskType: string;
    enabled: boolean;
    lastRun?: string;
    nextRun?: string;
    status: "idle" | "running" | "error";
  }>;
}

GET /api/scheduler/jobs/:id

Получить информацию о конкретной задаче

PUT /api/scheduler/jobs/:id

Обновить задачу (можно изменить schedule, enabled и т.д.)

DELETE /api/scheduler/jobs/:id

Удалить задачу из планировщика

POST /api/scheduler/jobs/:id/trigger

Ручной запуск задачи немедленно (вне расписания)Response:

{
  jobId: string;
  status: "triggered";
  startedAt: string;
}

Утилитарные endpoints

GET /api/health

Health check endpointResponse:

{
  status: "ok" | "error";
  timestamp: string;
  database: "connected" | "disconnected";
  services: {
    scrapers: "available" | "unavailable";
    embeddings: "available" | "unavailable";
  };
}

GET /api/stats

Статистика скрапингаResponse:

{
  totalProducts: number;
  totalCategories: number;
  lastScrape: {
    magnit?: string;
    "5ka"?: string;
  };
  priceHistory: {
    totalRecords: number;
    lastUpdate: string;
  };
}

GET /api/jobs/:jobId

Статус конкретной задачиResponse:

{
  jobId: string;
  status: "pending" | "running" | "completed" | "failed";
  startedAt: string;
  finishedAt?: string;
  progress?: {
    total: number;
    processed: number;
    percentage: number;
  };
  error?: string;
  result?: any;
}

Интеграция с n8n

Для интеграции с n8n и другими автоматизационными сервисами:

  1. HTTP Request Node в n8n:
  • URL: http://your-server:3000/api/scrapers/api/magnit
  • Method: POST
  • Body: JSON с параметрами
  1. Webhook для обратных вызовов (опционально):
  • POST /api/webhooks/:webhookId - регистрация webhook
  • При завершении задачи отправка POST запроса на указанный URL
  1. Polling статуса:
  • Использовать GET /api/jobs/:jobId для проверки статуса
  • Или настроить webhook для уведомлений

Аутентификация (опционально)

Для защиты API можно добавить:

  • API Key authentication через заголовок X-API-Key
  • JWT токены
  • Базовая аутентификация
// Пример middleware для API ключа
app.use('/api', (req, res, next) => {
  const apiKey = req.headers['x-api-key'];
  if (apiKey === process.env.API_KEY) {
    next();
  } else {
    res.status(401).json({ error: 'Unauthorized' });
  }
});

Важные моменты

  1. Приоритет API: Использовать API как основной метод получения данных для Магнита
  2. Авторизация и обход защиты:
  • Для API Магнита: Обязательно использовать Playwright для получения cookies и device-id перед API запросами - Инициализация сессии через браузер (один раз при старте) - Переиспользование cookies и device-id для всех запросов - Автоматическое обновление сессии при получении 403 Forbidden - Использование page.request API Playwright (рекомендуется) или передача cookies в axios
  • Для веба: Сохранение cookies/session для Playwright
  • Для приложений: Автоматизация входа через Appium
  1. Обработка ошибок: Robust error handling для сетевых проблем, изменения API и структуры сайтов
  2. Rate Limiting: Ограничение частоты запросов чтобы не блокировали (задержки между запросами)
  3. Мониторинг: Логирование всех операций скрапинга (API запросы, ответы, ошибки)
  4. Масштабируемость: Архитектура должна позволять легко добавлять новые магазины и методы скрапинга
  5. Единый формат данных: Нормализация данных из разных источников (API/Web/App) в единую структуру
  6. REST API для интеграций: Все операции доступны через REST API для интеграции с n8n и другими сервисами
  7. Асинхронная обработка: Задачи скрапинга выполняются асинхронно, возвращается jobId для отслеживания статуса
  8. Мониторинг задач: Возможность отслеживать прогресс выполнения задач через API endpoints

// Пример middleware для API ключаapp.use('/api', (req, res, next) => {const apiKey = req.headers['x-api-key'];if (apiKey === process.env.API_KEY) {next();} else {res.status(401).json({ error: 'Unauthorized' });}});


## Порядок разработки (Roadmap)

### Фаза 1: Минимальный прототип (MVP)

1.  Этап 1: База данных с Prisma - настройка БД
2.  Этап 2: API скрапинг Магнита - получение данных и сохранение в БД
3.  Этап 3: Embeddings - семантический поиск

### Фаза 2: Расширение функционала

4.  Этап 4: Полная реализация API скрапинга
5.  Этап 5: Веб и Android скрапинг (опционально)
6.  Этап 6: LangChain интеграция
7.  Этап 7: Планировщик и REST API
8.  Этап 8: Аналитика цен

**Текущий фокус**: Этап 1 (БД)  Этап 2 (API + БД)  Этап 3 (Embeddings)

## Важные моменты

1. **Приоритет разработки**: Сначала настройка БД (Этап 1), потом API скрапинг с сохранением в БД (Этап 2), затем embeddings (Этап 3). Веб/Android скрапинг - позже
2. **ORM выбор**: Используется Prisma (не TypeORM)
3. **Приоритет API**: Использовать API как основной метод получения данных для Магнита
4. **Авторизация и обход защиты**: 

            - **Для API Магнита**: Обязательно использовать Playwright для получения cookies и device-id перед API запросами
                    - Инициализация сессии через браузер (один раз при старте)
                    - Переиспользование cookies и device-id для всех запросов
                    - Автоматическое обновление сессии при получении 403 Forbidden
                    - Использование `page.request` API Playwright (рекомендуется) или передача cookies в axios
            - **Для веба**: Сохранение cookies/session для Playwright
            - **Для приложений**: Автоматизация входа через Appium

3. **Обработка ошибок**: Robust error handling для сетевых проблем, изменения API и структуры сайтов
4. **Rate Limiting**: Ограничение частоты запросов чтобы не блокировали (задержки между запросами)
5. **Мониторинг**: Логирование всех операций скрапинга (API запросы, ответы, ошибки)
6. **Масштабируемость**: Архитектура должна позволять легко добавлять новые магазины и методы скрапинга
7. **Единый формат данных**: Нормализация данных из разных источников (API/Web/App) в единую структуру
8. **Масштабируемость**: Архитектура должна позволять легко добавлять новые магазины и методы скрапинга