Разработка price-агрегатора (сравнение цен)
Price-агрегатор — сервис, который собирает цены на одинаковые товары с разных магазинов и показывает их рядом. Пользователь видит, где дешевле, и переходит туда по партнёрской ссылке. Технически это задача парсинга, нормализации данных и сопоставления товаров (matching). Каждый из этих этапов нетривиален в промышленном масштабе.
Источники данных
Данные о товарах и ценах поступают тремя способами:
Прайс-листы и фиды — магазин предоставляет YML, XML, CSV файл с актуальным ассортиментом. Самый надёжный источник: структурированные данные, официальное партнёрство, нет рисков бана. Яндекс.Маркет YML — де-факто стандарт для русскоязычного рынка.
API партнёров — некоторые магазины предоставляют REST API. Документация обычно слабая, лимиты запросов — жёсткие.
Веб-парсинг — для магазинов без фидов. Высокий риск: капча, rate limiting, изменение вёрстки, блокировка IP. Требует постоянной поддержки.
На старте агрегатора лучше работать только с фидами и API — это устойчивее. Парсинг подключать избирательно для ключевых источников.
Архитектура сборщика данных
Scheduler (Celery Beat / Laravel Scheduler)
↓ каждые N часов
FeedFetcher workers (по одному на источник)
↓
RawData storage (S3 или локальная FS)
↓
Parser workers (XML/CSV/JSON → нормализованные объекты)
↓
Normalizer (приведение единиц, очистка текста)
↓
Matcher (сопоставление с товарами в БД)
↓
PriceHistory (запись в timeseries)
↓
ElasticsearchIndexer (обновление индекса)
Очередь задач: Celery + Redis для Python-стека, Laravel Horizon + Redis для PHP-стека. Каждый фид обрабатывается независимо, ошибка в одном источнике не блокирует остальные.
Парсинг Яндекс.Маркет YML
YML — XML с жёсткой схемой. Критичные поля:
<offer id="12345" available="true">
<url>https://shop.example.com/product/12345</url>
<price>4990</price>
<currencyId>RUB</currencyId>
<categoryId>14</categoryId>
<name>Samsung Galaxy A55 128GB</name>
<vendor>Samsung</vendor>
<model>Galaxy A55</model>
<barcode>8806095076783</barcode>
<param name="Цвет">Синий</param>
<param name="Объём памяти">128 ГБ</param>
</offer>
Штрихкод (barcode) — лучший ключ для matching'а. GTIN/EAN уникален для каждой вариации товара. Если штрихкод есть у большинства поставщиков — сопоставление становится тривиальным.
Product Matching — сопоставление товаров
Это самая сложная часть агрегатора. Задача: определить, что Samsung Galaxy A55 128GB Синий от магазина A и Смартфон Samsung Galaxy A55 (SM-A556B) 128 Гб синий от магазина B — один и тот же товар.
Детерминированные методы:
- GTIN/EAN матчинг: если у обоих товаров есть штрихкод — однозначное совпадение
-
Артикул производителя (MPN):
SM-A556Bуникален в рамках бренда - URL-каноникализация: некоторые магазины включают GTIN в URL
Нечёткий матчинг (fuzzy):
from rapidfuzz import fuzz
def match_score(title_a: str, title_b: str, brand_a: str, brand_b: str) -> float:
if brand_a.lower() != brand_b.lower():
return 0.0
title_similarity = fuzz.token_sort_ratio(title_a, title_b)
return title_similarity / 100
Порог совпадения: 0.85+ считаем автоматическим матчем, 0.65–0.85 — отправляем на ручную проверку, ниже — новый товар.
ML-подход: эмбеддинги названий товаров (sentence-transformers, ruBERT) + cosine similarity. Значительно точнее fuzzy, особенно для разных формулировок одного товара. Модель обучается на исторически подтверждённых матчах.
Структура хранения:
canonical_products (id, gtin, mpn, brand, name, category_id, attrs JSONB)
source_offers (id, source_id, external_id, canonical_product_id, price, url, in_stock, updated_at)
match_candidates (offer_id, canonical_id, score, status) -- pending | approved | rejected
История цен
Основная ценность агрегатора — не только текущая цена, но и история изменений. Это означает, что каждое изменение цены должно записываться, не перезаписываться.
price_history (
id BIGSERIAL,
source_offer_id BIGINT,
price NUMERIC(12,2),
in_stock BOOLEAN,
recorded_at TIMESTAMPTZ DEFAULT NOW()
)
Для хранения timeseries данных в PostgreSQL применяем TimescaleDB — расширение, которое автоматически партиционирует таблицу по времени, ускоряя выборки за период. Альтернатива — InfluxDB или ClickHouse для высоких нагрузок.
График истории цен — стандартный компонент страницы товара. Используем Chart.js или Recharts, данные агрегируем по дням: SELECT date_trunc('day', recorded_at), min(price) FROM price_history WHERE ....
Поиск и сопоставление по категориям
У каждого магазина своя иерархия категорий. Смартфоны у одного — это Телефоны и смартфоны у другого. Нужен taxonomy mapping: дерево канонических категорий агрегатора + таблица соответствий с категориями источников.
canonical_categories (id, parent_id, name, slug)
source_category_mapping (source_id, source_category_id, canonical_category_id)
Маппинг делается один раз при подключении источника, потом поддерживается.
Партнёрские ссылки и монетизация
Основная модель монетизации агрегатора — CPC (Cost Per Click): магазин платит за каждый переход. Техническая реализация:
Пользователь кликает → /go/{offer_id}?ref=aggregator
→ запись клика (offer_id, user fingerprint, timestamp)
→ redirect 301 на URL магазина с utm_source параметрами
Клики считаются с дедупликацией: один пользователь за 24 часа = один засчитанный клик. Fingerprint: user_id (если авторизован) или хеш (IP + User-Agent + Accept-Language).
Для партнёров (магазинов) — личный кабинет со статистикой: показы, клики, CTR, расход бюджета.
Обновление цен
Частота обновления — критичный параметр. Устаревшие цены подрывают доверие. Типовые интервалы:
| Тип источника | Интервал обновления |
|---|---|
| YML-фид крупного магазина | Каждые 2–4 часа |
| API с лимитами | По лимиту, обычно 1–6 раз в сутки |
| Парсинг страницы | 1–2 раза в сутки |
| Real-time API (редко) | По webhook при изменении |
При изменении цены — инвалидация кеша страницы товара и пересчёт «минимальной цены» в индексе.
SEO-стратегия
Агрегаторы генерируют органический трафик на страницы конкретных товаров. Ключевые запросы: «[название товара] купить», «[название товара] цена», «[название товара] дешево».
- Страница каждого канонического товара: уникальный title с диапазоном цен
- Structured data:
Product+AggregateOfferсlowPrice,highPrice,offerCount - Статические страницы категорий с агрегированной статистикой (avg цена, кол-во предложений)
- Блог с обзорами и подборками — долгосрочный SEO-трафик
Сроки
- MVP: фиды от 3–5 источников, ручной matching, страницы товаров, базовый поиск — 8–12 недель
- Полноценный агрегатор: автоматический matching (fuzzy + ML), история цен с графиком, личный кабинет магазина, партнёрский трекинг — 20–30 недель
- Подключение каждого нового источника (парсинг): 3–7 рабочих дней в зависимости от сложности
Агрегатор цен — это продукт, который постоянно требует операционной поддержки: источники меняют структуру, товары нужно ремэтчить, новые магазины подключать. Это не разовая разработка, а платформа с командой поддержки.







