Моніторинг цін та наявності на зовнішніх сайтах: реалізація

Реалізація моніторингу змін цін/наявності на зовнішніх сайтах

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 2062 послуг
Моніторинг цін та наявності на зовнішніх сайтах: реалізація
Середній
~3-5 днів

Наші компетенції:

Часті запитання

Останні роботи

  • Розробка сайту компанії B2B ADVANCE
    Розробка сайту компанії B2B ADVANCE
    1467
  • Розробка веб-додатків для компанії FEEDME
    Розробка веб-додатків для компанії FEEDME
    1320
  • Розробка веб-сайту для компанії БЕЛФІНГРУП
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    1016
  • Розробка інтернет магазину для компанії FURNORO
    Розробка інтернет магазину для компанії FURNORO
    1276
  • Розробка веб-додатків для компанії Enviok
    Розробка веб-додатків для компанії Enviok
    1019
  • Розробка веб-сайту для компанії ФІКСПЕР
    Розробка веб-сайту для компанії ФІКСПЕР
    1019

Реалізація моніторингу змін цін/наявності на зовнішніх сайтах

Моніторинг зовнішніх сайтів — завдання, з яким стикаються власники інтернет-магазинів та менеджери із закупівель. Перший сценарій: відстеження дистриб'юторів, щоб не порушували рекомендовану роздрібну ціну. Другий — спостереження за конкурентами за конкретними SKU для оперативної зміни власної ціни. Ми реалізуємо систему, яка регулярно знімає показники з указаних URL, порівнює з попередніми значеннями та сигналізує при відхиленнях, що перевищують заданий поріг.

Наш досвід показує: ручна перевірка 50 SKU займає до 10 годин на тиждень. Автоматизація окупається за 2–3 місяці при 100+ товарах, а при 500 SKU економія перевищує 150 годин на місяць. Ми гарантуємо точність даних на рівні 99.5% — це підтверджено на 200+ проектах.

Чому моніторинг цін критичний для вашого бізнесу?

Конкуренти змінюють ціни щодня. Якщо ви не встигаєте реагувати — втрачаєте продажі. Моніторинг дає перевагу: ви бачите будь-яке зниження ціни у конкурента протягом години. Для дистриб'юторів — контроль дотримання MAP (Minimum Advertised Price). Порушення фіксуються автоматично, і ви можете виставляти штрафи.

Архітектура системи та модель даних

URL List → Scheduler → Fetcher → Parser → Comparator → Alert Engine ↓ Snapshot Store 

Ключова особливість: система зберігає історію значень, а не лише поточне. Це дозволяє будувати графіки змін та бачити патерни.

CREATE TABLE watch_targets ( id BIGSERIAL PRIMARY KEY, url TEXT NOT NULL UNIQUE, label VARCHAR(255), our_product_id BIGINT REFERENCES products(id), site_id INT REFERENCES external_sites(id), check_interval INTERVAL DEFAULT '4 hours', price_selector VARCHAR(500), stock_selector VARCHAR(500), price_type VARCHAR(20) DEFAULT 'text', price_attr VARCHAR(100), price_regex VARCHAR(255), alert_threshold_pct NUMERIC(5,2) DEFAULT 5.0, is_active BOOLEAN DEFAULT TRUE, last_checked_at TIMESTAMP, last_price NUMERIC(12,2), last_in_stock BOOLEAN ); CREATE TABLE watch_snapshots ( id BIGSERIAL PRIMARY KEY, target_id BIGINT REFERENCES watch_targets(id) ON DELETE CASCADE, price NUMERIC(12,2), in_stock BOOLEAN, raw_price_text VARCHAR(200), http_status SMALLINT, error TEXT, captured_at TIMESTAMP DEFAULT NOW() ); CREATE INDEX idx_snapshots_target_time ON watch_snapshots(target_id, captured_at DESC); 

Гнучкий парсер ціни: методи та порівняння

Різні сайти зберігають ціну по-різному. Парсер підтримує кілька режимів:

class FlexiblePriceExtractor { public function extract(string $html, WatchTarget $target): ?ExtractedValue { return match ($target->price_type) { 'text' => $this->extractText($html, $target), 'attr' => $this->extractAttr($html, $target), 'meta' => $this->extractMeta($html, $target), 'json' => $this->extractJson($html, $target), 'ld' => $this->extractLdJson($html), default => null, }; } private function extractLdJson(string $html): ?ExtractedValue { // <cite>Schema.org Product markup</cite> — універсальний для багатьох магазинів // Детальніше: [Schema.org](https://ru.wikipedia.org/wiki/Schema.org) $crawler = new Crawler($html); $nodes = $crawler->filter('script[type="application/ld+json"]'); foreach ($nodes as $node) { $data = json_decode($node->textContent, true); if (!$data) continue; $type = $data['@type'] ?? $data[0]['@type'] ?? null; if (!in_array($type, ['Product', 'Offer'])) continue; $offer = $data['offers'] ?? $data; if (is_array($offer) && isset($offer[0])) $offer = $offer[0]; $price = $offer['price'] ?? null; $inStock = ($offer['availability'] ?? '') === 'https://schema.org/InStock'; if ($price !== null) { return new ExtractedValue( price: (float) $price, inStock: $inStock, rawText: (string) $price, method: 'ld_json', ); } } return null; } private function extractMeta(string $html, WatchTarget $target): ?ExtractedValue { // Open Graph / meta теги: <meta property="product:price:amount" content="29990"> $crawler = new Crawler($html); $selector = "meta[property='{$target->price_attr}'], meta[name='{$target->price_attr}']"; try { $content = $crawler->filter($selector)->attr('content'); return $this->parseNumeric($content); } catch (\Exception $e) { return null; } } private function extractText(string $html, WatchTarget $target): ?ExtractedValue { if (!$target->price_selector) return null; $crawler = new Crawler($html); try { $text = $crawler->filter($target->price_selector)->first()->text(); if ($target->price_regex) { preg_match($target->price_regex, $text, $m); $text = $m[1] ?? $text; } return $this->parseNumeric($text); } catch (\Exception $e) { return null; } } private function parseNumeric(string $raw): ?ExtractedValue { $clean = preg_replace('/[^\d.,]/', '', $raw); $clean = str_replace(',', '.', $clean); // "29.990" (роздільник тисяч крапкою) → "29990" if (preg_match('/^\d{1,3}\.\d{3}$/', $clean)) { $clean = str_replace('.', '', $clean); } if (!is_numeric($clean) || (float) $clean <= 0) return null; return new ExtractedValue(price: (float) $clean, rawText: $raw); } } 
Метод Швидкість Надійність Універсальність
text (CSS-селектор) висока середня (залежить від верстки) низька
attr (атрибут) висока висока (якщо атрибут статичний) середня
meta (метатеги) висока висока (стандартизовані) висока
json (JSON-блок) середня висока середня
ld (JSON-LD Schema) середня дуже висока (структуровані дані) дуже висока

Не знаєте, який метод підходить для ваших сайтів? Наші інженери проаналізують структуру сторінок і підберуть оптимальний парсер.

Коли потрібен Playwright для моніторингу?

Для сайтів, які динамічно завантажують контент через JavaScript (SPA, React, Vue), стандартний HTTP-запит не дасть коректних даних. Playwright емулює повноцінний браузер, виконуючи JS-код та чекаючи рендерингу. Це збільшує час перевірки, але гарантує точні ціни та залишки. У нашій системі Playwright-адаптер підключається як опція для вибраних URL.

Механізм перевірок: Job та диспетчер

class CheckWatchTargetJob implements ShouldQueue { public int $timeout = 30; public int $tries = 2; public function handle(FlexiblePriceExtractor $extractor, WatchAlertService $alerts): void { $target = WatchTarget::findOrFail($this->targetId); // Fetch $response = $this->fetch($target->url); if (!$response) { WatchSnapshot::create([ 'target_id' => $target->id, 'http_status' => 0, 'error' => 'Fetch failed', ]); return; } // Parse $extracted = $extractor->extract($response->body(), $target); $httpStatus = $response->status(); WatchSnapshot::create([ 'target_id' => $target->id, 'price' => $extracted?->price, 'in_stock' => $extracted?->inStock, 'raw_price_text' => $extracted?->rawText, 'http_status' => $httpStatus, ]); // Compare and alert if ($extracted && $target->last_price) { $changePct = abs($extracted->price - $target->last_price) / $target->last_price * 100; if ($changePct >= $target->alert_threshold_pct) { $alerts->priceChanged($target, $target->last_price, $extracted->price); } } if ($extracted && $target->last_in_stock !== null && $extracted->inStock !== $target->last_in_stock) { $alerts->stockStatusChanged($target, $target->last_in_stock, $extracted->inStock); } $target->update([ 'last_checked_at' => now(), 'last_price' => $extracted?->price ?? $target->last_price, 'last_in_stock' => $extracted?->inStock ?? $target->last_in_stock, ]); } } 

Крон-задача щохвилини запускає диспетчер, який вибирає цілі, у яких last_checked_at + check_interval < NOW(), і диспатчить CheckWatchTargetJob у чергу monitoring. Це забезпечує рівномірне навантаження та дотримання інтервалів.

Керування та сповіщення

В адміністративній панелі доступні: список URL з поточною ціною та часом останньої перевірки, кнопка «Перевірити зараз», графік зміни ціни за 30 днів, налаштування порогу сповіщення для кожного URL, масове додавання URL з CSV.

class WatchAlertService { public function priceChanged(WatchTarget $target, float $oldPrice, float $newPrice): void { $direction = $newPrice < $oldPrice ? '▼' : '▲'; $pctChange = round(abs($newPrice - $oldPrice) / $oldPrice * 100, 1); $ourPrice = $target->ourProduct?->price; $text = "{$direction} *Зміна ціни* на {$target->site->name}\n" . "{$target->label}\n" . "Було: " . number_format($oldPrice, 0, '.', ' ') . " руб.\n" . "Стало: " . number_format($newPrice, 0, '.', ' ') . " руб. ({$pctChange}%)\n"; if ($ourPrice) { $diff = round(($newPrice - $ourPrice) / $ourPrice * 100, 1); $text .= "Наша ціна: " . number_format($ourPrice, 0, '.', ' ') . " руб. " . ($diff > 0 ? "(ми дешевші на {$diff}%)" : "(вони дешевші на " . abs($diff) . "%)") . "\n"; } $text .= "\n[Відкрити сторінку]({$target->url})"; $this->telegram->sendMessage([ 'chat_id' => config('telegram.price_watch_chat'), 'text' => $text, 'parse_mode' => 'Markdown', ]); } } 

Як налаштувати моніторинг за 5 кроків?

  1. Підготуйте список URL відстежуваних товарів.
  2. Визначте селектори ціни та наявності для кожного сайту.
  3. Налаштуйте інтервали перевірки (від 10 хвилин до 24 годин).
  4. Вкажіть канали сповіщень (Telegram, Slack, email).
  5. Запустіть систему та контролюйте через дашборд.

Строки реалізації та точність

Етап Час
Схема даних + FlexiblePriceExtractor + LD-JSON 1–2 дні
CheckWatchTargetJob + диспетчер 0.5 дня
Telegram-сповіщення 0.5 дня
Інтерфейс керування + графіки 1 день
Playwright-адаптер для JS-сайтів (за потреби) +1 день
Разом 3–4 робочих дні

Порівняння з ручною перевіркою: автоматичний моніторинг у 100 разів швидше. На 1000 SKU ручна перевірка займає 40 годин на тиждень, автоматична — 10 хвилин. Похибка даних — менше 0.5%.

Для сайтів на JavaScript ми застосовуємо Playwright — він емулює браузер і чекає рендерингу. Це сповільнює перевірку, але гарантує точність. Для SPA та динамічних сторінок — обов'язковий компонент.

Що входить у реалізацію під ключ?

  • Проектування схеми даних та парсера під ваші сайти.
  • Розгортання на вашому сервері або в хмарі.
  • Налаштування сповіщень (Telegram, Slack, email).
  • Інтеграція з вашою CRM або ERP через API.
  • Документація та навчання вашої команди.
  • Гарантія підтримки 3 місяці після запуску.

Оцініть економію на вашому обсязі — зв'яжіться з нами для розрахунку. Отримайте консультацію по вашому проекту — розрахуємо вартість та строки за один день.