Бот-парсер товарів постачальників: автоматичний збір даних

Менеджери витрачають до 3 годин на день на копіювання даних із кабінетів постачальників. При ручному введенні помилки сягають 15%, а застарілі ціни ведуть до прямих збитків. Результат — втрачені угоди та незадоволені клієнти. Ми автоматизуємо збір товарів, цін та залишків без участі людини. За час р

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

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

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

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

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

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

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

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1422
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1287
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    984
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1249
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    986
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Розробка веб-сайту для компанії ФІКСПЕР
    1000

Менеджери витрачають до 3 годин на день на копіювання даних із кабінетів постачальників. При ручному введенні помилки сягають 15%, а застарілі ціни ведуть до прямих збитків. Результат — втрачені угоди та незадоволені клієнти. Ми автоматизуємо збір товарів, цін та залишків без участі людини. За час роботи на ринку реалізували 30+ проєктів, скоротивши час збору даних для клієнтів на 80%. Наприклад, інтернет-магазин з асортиментом 5000 товарів щомісяця витрачав 40 годин на оновлення цін; після впровадження парсера процес займає 4 години автономно. Економія бюджету — до 30%, що в грошовому еквіваленті може становити, наприклад, 240 000 гривень на рік.

Згідно з визначенням, веб-скрапінг — це автоматичний збір даних з інтернету. Нижче — як це працює на практиці.

Чому ручний збір даних — це дорого?

80% часу йде на монотонні операції. Помилки в 15% записів через людський фактор. Втрата клієнтів через неактуальні ціни або відсутність товару на складі. Автоматизація знижує витрати на 30% і повністю виключає помилки введення.

Які дані ми збираємо

Тип даних Приклад Джерело
Основні поля Назва, ціна, SKU Сторінка списку товарів
Розширені Опис, характеристики, зображення Картка товару
Динамічні Залишки, статус наявності AJAX-запити або DOM

Як працює парсер і як ми обходимо захист?

Архітектура типового рішення:

  1. Планувальник (cron / Laravel Horizon) запускає завдання за розкладом.
  2. HTTP-клієнт (Guzzle / Playwright) завантажує сторінки постачальника.
  3. Парсер витягує дані через CSS-селектори або XPath.
  4. Нормалізатор приводить ціни, дати та валюти до єдиного формату.
  5. Дедуплікатор перевіряє товар за SKU та оновлює або створює запис.
  6. Сповіщення при помилках або значних змінах цін.

Сучасні постачальники використовують Cloudflare, капчі та динамічне завантаження. Ми застосовуємо ротацію резидентських проксі, емуляцію браузера Playwright/Puppeteer з випадковими рухами миші, затримки від 500 мс до 2 с і зміну User-Agent. Якщо сайт використовує JavaScript, Playwright завантажує сторінку як справжній браузер і повертає готовий DOM.

Що краще: Guzzle чи Playwright?

Критерій Guzzle (PHP) Playwright (Node)
Швидкість Висока (немає браузера) Середня (запуск браузера)
Робота з JS Не підтримує Повна емуляція
Ресурси Мінімальні Вимагає більше ОЗУ
Складність парсингу Достатньо для статики Необхідний для SPA

Guzzle обробляє статичні сторінки в 2 рази швидше Playwright. Комбінований підхід — Guzzle для статики, Playwright для SPA — дає найкращий результат.

Технічна реалізація

Базовий парсер на PHP

// app/Services/Scrapers/SupplierScraper.php use GuzzleHttp\Client; use Symfony\Component\DomCrawler\Crawler; class SupplierScraper { private Client $client; public function __construct( private string $baseUrl, private array $proxyPool = [] ) { $this->client = new Client([ 'timeout' => 15, 'connect_timeout' => 5, 'headers' => [ 'User-Agent' => $this->randomUserAgent(), 'Accept-Language' => 'ru-RU,ru;q=0.9', 'Accept' => 'text/html,application/xhtml+xml', ], ]); } public function scrapeProductList(string $categoryUrl): array { $html = $this->fetchWithRetry($categoryUrl); $crawler = new Crawler($html); return $crawler->filter('.product-card')->each(function (Crawler $node) { return [ 'url' => $node->filter('a.product-link')->attr('href'), 'title' => trim($node->filter('.product-title')->text()), 'price' => $this->parsePrice($node->filter('.price')->text()), 'sku' => $node->filter('[data-sku]')->attr('data-sku'), ]; }); } public function scrapeProductDetail(string $productUrl): array { $html = $this->fetchWithRetry($this->baseUrl . $productUrl); $crawler = new Crawler($html); return [ 'title' => $crawler->filter('h1.product-name')->text(), 'description' => $crawler->filter('.description')->html(), 'price' => $this->parsePrice($crawler->filter('.current-price')->text()), 'images' => $crawler->filter('.gallery img')->each( fn(Crawler $img) => $img->attr('src') ), 'specs' => $this->extractSpecs($crawler), 'in_stock' => $crawler->filter('.in-stock')->count() > 0, 'sku' => $crawler->filter('[itemprop="sku"]')->text(''), ]; } private function extractSpecs(Crawler $crawler): array { $specs = []; $crawler->filter('.specs-table tr')->each(function (Crawler $row) use (&$specs) { $key = trim($row->filter('td:first-child')->text('')); $val = trim($row->filter('td:last-child')->text('')); if ($key && $val) { $specs[$key] = $val; } }); return $specs; } private function fetchWithRetry(string $url, int $attempts = 3): string { $proxy = $this->proxyPool ? $this->randomProxy() : null; for ($i = 0; $i < $attempts; $i++) { try { $options = $proxy ? ['proxy' => $proxy] : []; $response = $this->client->get($url, $options); return (string) $response->getBody(); } catch (\Exception $e) { if ($i === $attempts - 1) throw $e; sleep(rand(2, 5)); } } } private function parsePrice(string $text): float { return (float) preg_replace('/[^\d.,]/', '', str_replace(',', '.', $text)); } private function randomUserAgent(): string { $agents = [ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15', ]; return $agents[array_rand($agents)]; } } 

Job для фонової обробки

// app/Jobs/ScrapeSupplierProducts.php class ScrapeSupplierProducts implements ShouldQueue { use Queueable; public int $tries = 2; public int $timeout = 300; public function __construct( private int $supplierId, private string $categoryUrl ) {} public function handle( SupplierScraper $scraper, ProductImportService $importer ): void { $products = $scraper->scrapeProductList($this->categoryUrl); foreach ($products as $productPreview) { ScrapeSupplierProductDetail::dispatch( $this->supplierId, $productPreview['url'] )->onQueue('scraper-detail'); usleep(rand(500000, 1500000)); } } } 

Playwright для JS-сайтів

// scraper/playwright-worker.js const { chromium } = require('playwright'); async function scrapeProduct(url) { const browser = await chromium.launch({ headless: true }); const context = await browser.newContext({ userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)...', viewport: { width: 1366, height: 768 }, }); const page = await context.newPage(); await page.goto(url, { waitUntil: 'networkidle' }); const product = await page.evaluate(() => ({ title: document.querySelector('h1')?.textContent, price: document.querySelector('.price')?.textContent, images: [...document.querySelectorAll('.gallery img')].map(i => i.src), })); await browser.close(); return product; } 

PHP викликає Node-процес через proc_open або HTTP-мікросервіс.

Дедуплікація та оновлення

// app/Services/ProductImportService.php class ProductImportService { public function upsert(int $supplierId, array $data): void { $product = SupplierProduct::updateOrCreate( [ 'supplier_id' => $supplierId, 'supplier_sku' => $data['sku'], ], [ 'title' => $data['title'], 'price' => $data['price'], 'in_stock' => $data['in_stock'], 'description' => $data['description'], 'images' => json_encode($data['images']), 'specs' => json_encode($data['specs']), 'scraped_at' => now(), ] ); if ($product->wasChanged('price')) { $change = abs($product->price - $product->getOriginal('price')); if ($change / $product->getOriginal('price') > 0.05) { PriceChangedNotification::dispatch($product); } } } } 

Розклад запусків

// app/Console/Kernel.php protected function schedule(Schedule $schedule): void { $schedule->command('scraper:supplier --supplier=1') ->dailyAt('03:00') ->withoutOverlapping(); $schedule->command('scraper:supplier --supplier=1 --prices-only') ->everyFourHours() ->withoutOverlapping(); } 
Приклад структури даних для парсингу
Поле Тип Приклад
sku string ART-12345
title string Смартфон XYZ
price float 19999.99
in_stock bool true

Процес розробки та терміни

  1. Аналітика (1-2 дні) — вивчаємо структуру сайту постачальника, визначаємо типи захисту, узгоджуємо поля.
  2. Проєктування (1 день) — обираємо стек, проєктуємо архітектуру (черги, розклад, схема даних).
  3. Реалізація (3-7 днів) — пишемо парсери, нормалізатори, дедуплікацію, інтеграцію з вашою системою.
  4. Тестування (1-2 дні) — прогоняємо на реальних даних, перевіряємо коректність та стійкість.
  5. Деплой (1 день) — налаштовуємо оточення, моніторинг, сповіщення, передаємо документацію.

Терміни орієнтовно

  • Базовий парсер одного постачальника (статичний HTML, 5-10 полів): від 3 до 5 робочих днів.
  • Парсер зі складним захистом або JS-рендерингом: від 7 до 14 днів.
  • Інтеграція з кількома постачальниками в єдиний pipeline: від 10 робочих днів.

Вартість розраховується індивідуально після аудиту цільових сайтів.

Що входить у результат

  • Парсер з відкритим вихідним кодом (PHP + Laravel або Node.js).
  • Інтеграція з вашою БД через API або прямий імпорт.
  • Налаштування розкладу та моніторингу.
  • Документація з запуску та підтримки.
  • Гарантія стабільної роботи 30 днів.

Типові помилки при розробці парсерів

  • Ігнорування rate limit — сайт блокує IP після 100 запитів. Рішення: додавати випадкові затримки та ротацію.
  • Жорстка прив'язка до структури HTML — найменша зміна дизайну ламає парсер. Рішення: використовувати data-атрибути або XPath.
  • Відсутність обробки помилок — при збої одного постачальника падає вся черга. Рішення: ізолювати завдання та додати повторні спроби.
  • Збір усіх даних в одну таблицю без нормалізації — дублі та пробіли. Рішення: використовувати SKU як унікальний ключ.

Зв'яжіться з нами для консультації — оцінимо ваш проєкт і запропонуємо рішення під ключ. Замовте розробку парсера та отримайте готове рішення, яке заощадить ваш час і гроші.