39 KiB
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 - начало)
Цель: Настроить БД для сохранения товаров
- Инициализация TypeScript проекта
- Настройка PostgreSQL с pgvector (Docker)
- Установка и настройка Prisma ORM
- Создание Prisma schema:
- Модель Store (магазины)
- Модель Category (категории)
- Модель Product (товары) - базовые поля без embeddings
- Модель PriceHistory (история цен)
- Модель ScrapingSession (сессии скрапинга)
- Создание миграций Prisma
- Настройка Prisma Client и подключения к БД
- Тестирование подключения и базовых операций
Этап 2: API скрапинг Магнита и сохранение в БД (MVP - продолжение)
Цель: Получить данные через API Магнита и сохранить в БД
- Настройка базовых зависимостей (playwright, axios, dotenv)
- Реализация MagnitApiScraper с обходом защиты (Playwright для сессии)
- Метод инициализации сессии через Playwright
- Метод поиска товаров (POST /webgate/v2/goods/search)
- Получение 2-3 страниц товаров для тестирования
- Парсинг и нормализация данных из API ответа
- Интеграция MagnitApiScraper с БД через Prisma
- Сохранение товаров в БД через Prisma Client
- Сохранение истории цен при каждом скрапинге
- Тестирование на нескольких категориях товаров
- Проверка сохранения данных в PostgreSQL
Этап 3: Embeddings и векторный поиск
Цель: Добавить embeddings для товаров и категорий
- Установка и настройка pgvector расширения в PostgreSQL
- Обновление Prisma schema:
- Добавление поля
embedding vector(1536)в модель Product - Добавление поля
embedding vector(1536)в модель Category
- Создание миграции для pgvector
- Интеграция с моделью для embeddings (OpenAI embeddings или Ollama)
- Реализация EmbeddingService:
- Генерация embeddings для названий и описаний товаров
- Батчовая обработка для эффективности
- Обновление индексов pgvector для быстрого поиска
- Функции векторного поиска в PostgreSQL через Prisma Raw queries
Этап 4: API-скрапинг - полная реализация
- Базовый класс BaseApiScraper с общим функционалом
- HTTP клиент (axios) с настройкой заголовков
- Retry logic и обработка ошибок
- Rate limiting
- Интеграция с Playwright для получения сессий
- 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 ответов в единый формат
- Управление жизненным циклом браузера (переиспользование сессии)
- 5kaApiScraper (если доступен API, аналогичная структура)
- Сервис парсинга и нормализации товаров
- Сохранение данных в БД
Этап 5: Расширение функционала (после MVP)
Этап 5.1: 5ka API скрапинг
- Реализация 5kaApiScraper (если доступен API)
- Интеграция с БД через Prisma
Этап 5.2: Веб-скрапинг (Playwright) - fallback/дополнение
- Базовый класс BaseWebScraper с общим функционалом
- MagnitWebScraper с авторизацией (если API недоступен)
- 5kaWebScraper с авторизацией
- Интеграция как резервный метод
Этап 5.3: Android-скрапинг (Appium)
- Настройка Appium и WebDriverIO
- Базовый класс BaseAppScraper
- MagnitAppScraper для Android приложения
- 5kaAppScraper для Android приложения
- Сервис авторизации в приложениях
Этап 6: LangChain интеграция
- Настройка LangChain SQL Agent
- Подключение к PostgreSQL через Prisma
- Промпты для работы с товарами и ценами
- Реализация запросов типа "самый дешевый попкорн"
Этап 7: Планировщик и автоматизация
- SchedulerService с cron-like функционалом
- node-cron для расписаний
- Управление задачами (запуск, остановка, статус)
- Конфигурация расписаний через конфиг-файл
- REST API Server для внешних интеграций
- HTTP сервер (Express или Fastify)
- API endpoints для триггеров из n8n и других сервисов
- Документация API (OpenAPI/Swagger)
- API Endpoints:
POST /api/scrapers/api/magnit- Запуск API-скрапинга МагнитаPOST /api/scrapers/api/5ka- Запуск API-скрапинга 5kaPOST /api/scrapers/web/magnit- Запуск веб-скрапинга МагнитаPOST /api/scrapers/web/5ka- Запуск веб-скрапинга 5kaPOST /api/scrapers/android/magnit- Запуск Android-скрапинга МагнитаPOST /api/scrapers/android/5ka- Запуск Android-скрапинга 5kaPOST /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 checkGET /api/stats- Статистика скрапинга
- Конфигурация расписаний обновления
- Логирование и обработка ошибок
- CLI для ручного запуска скриптов (как альтернатива API)
Этап 8: Обработка истории цен
- Автоматическое создание записей в price_history (уже в Этапе 2)
- Анализ изменений цен
- Уведомления о значительных изменениях (опционально)
Технологический стек
- 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 Магнита защищен и требует:
- Обязательные заголовки:
x-device-id- Device ID из cookiemg_udi(формат UUID:9D4909B8-9F79-5B1D-8457-EA519EC9AA3F)x-client-name: magnitx-device-platform: Webx-app-version: 2025.12.12-16.56(актуальная версия)x-new-magnit: truex-platform-version: Windows Chrome 130referer: https://magnit.ru/user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...
- Обязательные cookies:
shopCode=992301mg_udi- Device ID (UUID формат)oxxfgh,uwyii,uwyiert- сессионные cookiesnmg_dt,nmg_sp,mg_uac,mg_adlt- другие служебные cookies
- Заголовки браузера:
accept: */*accept-language: ru,en-US;q=0.9,en;q=0.8accept-encoding: gzip, deflate, br, zstdsec-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 и другими автоматизационными сервисами:
- HTTP Request Node в n8n:
- URL:
http://your-server:3000/api/scrapers/api/magnit - Method: POST
- Body: JSON с параметрами
- Webhook для обратных вызовов (опционально):
POST /api/webhooks/:webhookId- регистрация webhook- При завершении задачи отправка POST запроса на указанный URL
- 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' });
}
});
Важные моменты
- Приоритет API: Использовать API как основной метод получения данных для Магнита
- Авторизация и обход защиты:
- Для API Магнита: Обязательно использовать Playwright для получения cookies и device-id перед API запросами
- Инициализация сессии через браузер (один раз при старте)
- Переиспользование cookies и device-id для всех запросов
- Автоматическое обновление сессии при получении 403 Forbidden
- Использование
page.requestAPI Playwright (рекомендуется) или передача cookies в axios - Для веба: Сохранение cookies/session для Playwright
- Для приложений: Автоматизация входа через Appium
- Обработка ошибок: Robust error handling для сетевых проблем, изменения API и структуры сайтов
- Rate Limiting: Ограничение частоты запросов чтобы не блокировали (задержки между запросами)
- Мониторинг: Логирование всех операций скрапинга (API запросы, ответы, ошибки)
- Масштабируемость: Архитектура должна позволять легко добавлять новые магазины и методы скрапинга
- Единый формат данных: Нормализация данных из разных источников (API/Web/App) в единую структуру
- REST API для интеграций: Все операции доступны через REST API для интеграции с n8n и другими сервисами
- Асинхронная обработка: Задачи скрапинга выполняются асинхронно, возвращается jobId для отслеживания статуса
- Мониторинг задач: Возможность отслеживать прогресс выполнения задач через 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. **Масштабируемость**: Архитектура должна позволять легко добавлять новые магазины и методы скрапинга