Разработка парсера цен конкурентов для мониторинга

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.
Разработка и обслуживание любых видов сайтов:
Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Разработка парсера цен конкурентов для мониторинга
Средняя
~3-5 рабочих дней
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1214
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    852
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    823
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    815

Разработка парсера цен конкурентов для мониторинга

Мониторинг цен конкурентов решает одну конкретную задачу: знать, когда и насколько конкурент изменил цену, раньше чем это заметят покупатели. Для этого нужен не просто парсер, а система с историей изменений, аналитикой и оповещениями в реальном времени.

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

Ключевое отличие от разового парсера — приоритизация товаров. Популярные позиции нужно проверять раз в час, длинный хвост каталога — раз в сутки. Это снижает нагрузку на источник и ускоряет реакцию на важные изменения.

[Scheduler]
  ├── High priority queue (топ-товары, каждый час)
  └── Low priority queue  (остальные, раз в сутки)
        ↓
[Fetcher] → [Parser] → [Change Detector] → [Alert Engine]
                              ↓
                       [price_history table]

Change Detector сравнивает новую цену с последней записью в истории. При изменении — запись в price_history и событие в очередь алертов. Без изменений — только обновление last_checked_at, чтобы не раздувать историю.

Парсинг цен: технические нюансы

Цены на сайтах бывают представлены по-разному:

  • В HTML (простой случай) — CSS-селектор .product-price или атрибут data-price
  • В JSON-LD (schema.org Product) — надёжно, не ломается при редизайне
  • Через XHR API — перехват сетевых запросов через Playwright
  • Динамически через JS после загрузки — нужен headless browser

JSON-LD — наиболее стабильный источник для парсинга цен. Многие SEO-оптимизированные магазины добавляют микроразметку schema.org для поисковых роботов:

import * as cheerio from 'cheerio';

interface PriceData {
  price: number;
  priceSale?: number;
  currency: string;
  inStock: boolean;
}

function extractPriceFromJsonLd(html: string): PriceData | null {
  const $ = cheerio.load(html);

  for (const scriptEl of $('script[type="application/ld+json"]').toArray()) {
    try {
      const data = JSON.parse($(scriptEl).html() ?? '{}');
      const product = data['@type'] === 'Product' ? data :
        (Array.isArray(data['@graph'])
          ? data['@graph'].find((n: { '@type': string }) => n['@type'] === 'Product')
          : null);

      if (product?.offers) {
        const offer = Array.isArray(product.offers) ? product.offers[0] : product.offers;
        return {
          price: parseFloat(offer.price),
          currency: offer.priceCurrency ?? 'RUB',
          inStock: offer.availability?.includes('InStock') ?? true,
        };
      }
    } catch { continue; }
  }

  return null;
}

Обработка форматов цен в тексте: "1 299,00 ₽", "$12.99", "€ 9,90" — нормализация через regex. Хранить как DECIMAL(10,2) с отдельным полем currency. Следить за тремя уровнями: цена без скидки (price_original), цена со скидкой (price_sale), цена по карте лояльности (часто третья скрытая цена).

База данных

CREATE TABLE monitored_products (
  id              SERIAL PRIMARY KEY,
  source          VARCHAR(100) NOT NULL,
  external_id     VARCHAR(255) NOT NULL,
  title           TEXT,
  url             TEXT NOT NULL,
  priority        SMALLINT DEFAULT 5,  -- 1=высший, 10=низший
  check_interval  INT DEFAULT 360,     -- минуты
  last_checked_at TIMESTAMPTZ,
  UNIQUE(source, external_id)
);

CREATE TABLE price_history (
  id             BIGSERIAL PRIMARY KEY,
  product_id     INT REFERENCES monitored_products(id),
  price          DECIMAL(10,2),
  price_original DECIMAL(10,2),
  in_stock       BOOLEAN,
  currency       VARCHAR(3) DEFAULT 'RUB',
  recorded_at    TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX ON price_history(product_id, recorded_at DESC);

-- Быстрый доступ к актуальной цене без JOIN с историей
ALTER TABLE monitored_products
  ADD COLUMN current_price DECIMAL(10,2),
  ADD COLUMN current_in_stock BOOLEAN;

Детектор изменений

async function processNewPrice(
  productId: number,
  newPrice: number,
  newInStock: boolean
): Promise<{ changed: boolean; delta?: number }> {
  const product = await db.monitoredProducts.findById(productId);

  const priceChanged = product.currentPrice !== newPrice;
  const stockChanged = product.currentInStock !== newInStock;

  if (!priceChanged && !stockChanged) {
    // Только обновить время проверки
    await db.monitoredProducts.update(productId, { lastCheckedAt: new Date() });
    return { changed: false };
  }

  // Записать в историю
  await db.priceHistory.create({
    productId,
    price: newPrice,
    inStock: newInStock,
    recordedAt: new Date(),
  });

  // Обновить текущие значения
  await db.monitoredProducts.update(productId, {
    currentPrice: newPrice,
    currentInStock: newInStock,
    lastCheckedAt: new Date(),
  });

  const delta = product.currentPrice
    ? ((newPrice - product.currentPrice) / product.currentPrice) * 100
    : 0;

  return { changed: true, delta };
}

Алерты и пороги

Конфигурируемые правила срабатывания:

  • Цена снизилась более чем на X% (например, 5% или 10%)
  • Цена опустилась ниже вашей цены на аналогичный товар
  • Товар появился или исчез из наличия
  • Цена изменилась у N+ конкурентов одновременно (признак рыночного сдвига)
  • Цена достигла исторического минимума за последние 90 дней
async function checkAlertRules(productId: number, delta: number): Promise<void> {
  const rules = await db.alertRules.findAll({ productId, active: true });

  for (const rule of rules) {
    const triggered =
      (rule.type === 'price_drop_percent' && delta < -rule.threshold) ||
      (rule.type === 'below_my_price' && await isPriceBelowMyPrice(productId)) ||
      (rule.type === 'out_of_stock' && newInStock === false);

    if (triggered) {
      await sendAlert(rule, productId, delta);
    }
  }
}

Доставка: Telegram-бот (мгновенно через Bot API), email-дайджест (раз в день), webhook в систему управления ценами (для автоматической реакции).

Дашборд аналитики

Минимально необходимые экраны:

Таблица мониторинга — все отслеживаемые товары с текущей ценой конкурента, вашей ценой, разницей в процентах и трендом (стрелка вверх/вниз).

График цен — цена конкурента vs ваша цена за выбранный период. Recharts LineChart с двумя линиями и маркерами изменений.

Лента алертов — последние 50 изменений с фильтрацией по источнику и типу изменения.

Быстрая реализация дашборда — Metabase подключённый к PostgreSQL. Кастомный React-интерфейс с Recharts нужен если дашборд встраивается в существующую систему управления ассортиментом.

Сроки и масштаб

Масштаб Источников Товаров Срок
Малый 1–3 до 10k 5–8 дней
Средний 3–10 10k–100k 2–3 недели
Крупный 10+ 100k+ 4–6 недель

Для 100k+ товаров с историей за год — ClickHouse вместо PostgreSQL для хранения price_history: аналитические запросы (агрегация за период, поиск минимума) на больших объёмах работают на порядок быстрее. PostgreSQL остаётся для оперативных данных и конфигурации.