Ми розробляємо системи моніторингу цін конкурентів, які вирішують конкретне завдання: знати, коли і наскільки конкурент змінив ціну, раніше ніж це помітять покупці. Без автоматизації моніторингу ви ризикуєте пропустити момент зниження ціни на ключовий товар. Ручна перевірка десятків тисяч позицій нереальна, а разовий аудит дає лише миттєвий зріз. За час роботи ми запустили понад 30 таких проектів для e-commerce — від невеликих магазинів до великих маркетплейсів. Наш досвід дозволяє гарантувати стабільну роботу парсера навіть при зміні структури сайту. Якщо вам потрібен надійний інструмент, зв'яжіться з нами — ми оцінимо ваш проект і запропонуємо оптимальне рішення.
Архітектура системи моніторингу
Ключова відмінність від разового парсера — пріоритизація товарів. Популярні позиції потрібно перевіряти раз на годину, довгий хвіст каталогу — раз на добу. Це знижує навантаження на джерело і прискорює реакцію на важливі зміни. Черга завдань розподіляється за пріоритетами, що дозволяє обробляти до 10 000 товарів на хвилину без перевантаження.
[Scheduler]
├── High priority queue (топ-товари, щогодини)
└── Low priority queue (інші, раз на добу)
↓
[Fetcher] → [Parser] → [Change Detector] → [Alert Engine]
↓
[price_history table]
Change Detector порівнює нову ціну з останнім записом в історії. При зміні — запис у price_history і подія в чергу алертів. Без змін — лише оновлення last_checked_at, щоб не роздувати історію. Такий підхід дозволяє зберігати до 5 років історії при мінімальному обсязі даних.
Чому JSON-LD — найкраще джерело для парсингу цін?
Ціни на сайтах бувають представлені по-різному. В HTML — CSS-селектор .product-price або атрибут data-price. В Schema.org — надійно, не ламається при редизайні. Через XHR API — перехоплення мережевих запитів через Playwright. Динамічно через JS після завантаження — потрібен headless browser. JSON-LD — найбільш стабільне джерело: багато SEO-оптимізованих магазинів додають мікророзмітку для пошукових роботів. Помилка при парсингу через HTML може сягати 10%, а через JSON-LD — менше 0.5%.
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), ціна за карткою лояльності (часто третя прихована ціна).
| Джерело | Стабільність | Швидкість | Складність |
|---|---|---|---|
| HTML | Середня | Висока | Низька |
| JSON-LD | Висока | Висока | Низька |
| XHR API | Середня | Середня | Середня |
| Headless browser | Низька | Низька | Висока |
Механізм детектора змін
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 потрібен, якщо дашборд вбудовується в існуючу систему управління асортиментом.
Процес розробки
- Аналітика — вивчаємо цільові сайти, визначаємо пріоритети товарів, погоджуємо алерти та дашборд.
- Проектування — проектуємо архітектуру парсера, базу даних, правила алертів.
- Реалізація — пишемо код, налаштовуємо черги, інтеграції.
- Тестування — запускаємо на реальних даних, перевіряємо точність та стабільність.
- Деплой і підтримка — розгортаємо на вашому сервері або хмарі, передаємо документацію, навчаємо.
Що входить у роботу
- Вихідний код парсера з відкритою документацією
- Доступ до дашборду та API
- Інструкція з експлуатації
- Гарантія стабільної роботи 3 місяці
- Підтримка при змінах структури сайтів (до 5 адаптацій на місяць)
Строки та масштаб
| Масштаб | Джерел | Товарів | Строк |
|---|---|---|---|
| Малий | 1–3 | до 10k | 5–8 днів |
| Середній | 3–10 | 10k–100k | 2–3 тижні |
| Крупний | 10+ | 100k+ | 4–6 тижнів |
Для 100k+ товарів з історією за рік — ClickHouse замість PostgreSQL для зберігання price_history: аналітичні запити (агрегація за період, пошук мінімуму) на великих обсягах працюють на порядок швидше. PostgreSQL залишається для оперативних даних і конфігурації.
Приклад архітектури для крупного проекту
Для масштабів понад 100k товарів ми використовуємо розподілені черги RabbitMQ та мікросервіси на Go для парсингу. Це дозволяє горизонтально масштабуватися до 1 млн товарів з часом оновлення не більше 1 години.Зв'яжіться з нами для консультації — ми оцінимо ваш проект і запропонуємо оптимальне рішення. Отримайте надійний парсер цін під ключ з гарантією стабільності.







