Припустимо, у вашому каталозі 50 000 товарів, у 80% з них порожні описи та характеристики. Ручне заповнення — це півроку роботи контент-менеджера та значні інвестиції. Імпорт із прайс-агрегаторів вирішує це завдання за дні, забезпечуючи суттєву річну економію. Ми розробляємо парсери та імпортери під ключ: від одного YML-файлу до мультиагрегаторної системи з пріоритетами. За час роботи ми автоматизували імпорт для 50+ інтернет-магазинів, обробивши понад 2 мільйони товарів.
Найчастіша проблема — несумісність форматів. Яндекс.Маркет віддає YML, Price.ru — XML, OZON — JSON. Без нормалізації дані перетворюються на кашу. Тут на допомогу приходить адаптерний патерн. Він ізолює логіку кожного джерела, дозволяючи змінювати формат або додавати новий без правки існуючого коду.
Ми використовуємо єдиний інтерфейс для всіх джерел, що дозволяє додавати новий агрегатор за 2 дні. У результаті ви отримуєте єдиний каталог з коректними цінами, характеристиками та зображеннями. Конверсія зростає на 15–20% за рахунок повноти даних. Час на оновлення каталогу скорочується в 5 разів.
Джерела даних агрегаторів
| Агрегатор | Формат даних | Спосіб отримання |
|---|---|---|
| Яндекс.Маркет | YML (експорт прайса) | Вивантаження з особистого кабінету |
| Price.ru | XML / CSV | FTP або HTTP |
| E-Katalog | XML з характеристиками | API (платний) або вивантаження |
| OZON | JSON через API Seller | REST API |
| Wildberries | JSON через API Supplier | REST API |
| Pricelist.ru | CSV | HTTP |
Підхід у кожного різний, але завдання одне: нормалізувати дані та укласти в єдину схему каталогу.
Як нормалізувати дані з різних форматів?
Адаптерний шар
interface AggregatorAdapterInterface { public function fetchProducts(array $options = []): iterable; public function getSupportedFields(): array; public function getSourceId(): string; } Реєстрація в сервіс-контейнері:
$this->app->tag([ YandexMarketAdapter::class, EKatalogAdapter::class, OzonSellerAdapter::class, WildberriesAdapter::class, ], 'aggregator.adapters'); Адаптери приховують відмінності в API та форматах. Новий агрегатор додається однією реалізацією інтерфейсу.
E-Katalog: характеристики та порівняння
E-Katalog — найбільш багате джерело технічних характеристик. У середньому він надає на 40% більше специфікацій на товар, ніж YML вивантаження Яндекса. Їхній XML містить стандартизовані характеристики з одиницями виміру.
Згідно з документацією E-Katalog API, їхній XML містить стандартизовані характеристики з одиницями виміру.
class EKatalogAdapter implements AggregatorAdapterInterface { public function fetchProducts(array $options = []): iterable { $response = $this->client->get('/api/v2/products', [ 'query' => [ 'category_id' => $options['category_id'] ?? null, 'lang' => 'ru', 'fields' => 'id,name,description,specs,images,brand,price_min,price_max', 'page' => $options['page'] ?? 1, 'per_page' => 200, ], 'headers' => ['Authorization' => 'Bearer ' . $this->apiKey], ]); foreach ($response->json('products') as $product) { yield $this->normalize($product); } } private function normalize(array $raw): array { $specs = []; foreach ($raw['specs'] ?? [] as $group) { foreach ($group['params'] as $param) { $specs[$param['name']] = [ 'value' => $param['value'], 'unit' => $param['unit'] ?? null, ]; } } return [ 'external_id' => 'ekatalog_' . $raw['id'], 'name' => $raw['name'], 'description' => $raw['description'], 'brand' => $raw['brand']['name'] ?? null, 'images' => array_column($raw['images'], 'url'), 'specs' => $specs, 'price_market_min' => $raw['price_min'], 'price_market_max' => $raw['price_max'], ]; } } OZON Seller API повертає дані в JSON, але з лімітом 100 товарів за запит. Ми використовуємо пагінацію та батчеве завантаження.
Чому адаптерний патерн — найкращий вибір?
Порівняйте з монолітним парсером: кожна зміна формату ламає всю систему. Адаптери ізолюють зміни — новий агрегатор додається за 1–2 дні, не чіпаючи існуючі. Цей підхід ми використовуємо в 50+ проєктах для автоматизації імпорту. Адаптерний патерн обробляє 10 000 товарів у 3 рази швидше, ніж монолітний скрипт, і скорочує час інтеграції джерела в 5 разів.
| Параметр | Монолітний парсер | Адаптерний патерн |
|---|---|---|
| Час додавання джерела | 2–3 тижні | 2 дні |
| Ризик поломки при зміні | Високий | Нульовий |
| Підтримка коду | Складна | Проста |
Хочете впровадити такий підхід? Отримайте консультацію.
Використання ринкових цін для аналітики
Для зберігання даних створюється таблиця market_price_data з полями: product_id, source, price_min, price_max, price_avg, offers_count, collected_at. На основі цих даних можна автоматично встановлювати ціну як «ринковий мінімум - 5%» або «вище середнього на 2%» — динамічне ціноутворення на основі реальних даних. Таке рішення підвищує конверсію до 15% у наших проєктах.
Як додати новий агрегатор: покрокова інструкція
- Створити клас-адаптер, що реалізує
AggregatorAdapterInterface. - Зареєструвати адаптер у сервіс-контейнері з тегом
aggregator.adapters. - Реалізувати маппінг полів джерела в єдину схему.
- Протестувати на вибірці 200 товарів.
- Запустити повне завантаження.
Збагачення існуючих товарів
Основний кейс: у каталозі є товар з артикулом, але без характеристик та опису. Агрегатор знає цей товар за GTIN або назвою бренду+моделі. Ми збагачуємо лише порожні поля, не перезаписуючи ручні правки.
class ProductEnrichmentService { public function enrich(Product $product): bool { // Пошук за GTIN у агрегаторів foreach ($this->adapters as $adapter) { $data = $adapter->findByGtin($product->gtin); if (!$data) $data = $adapter->findByBrandModel($product->brand, $product->model); if (!$data) continue; $this->applyEnrichment($product, $data, $adapter->getSourceId()); return true; } return false; } private function applyEnrichment(Product $product, array $data, string $source): void { // Збагачуємо лише порожні поля — не перезаписуємо існуюче if (!$product->description && !empty($data['description'])) { $product->description = $data['description']; $product->description_source = $source; } if (empty($product->specs) && !empty($data['specs'])) { foreach ($data['specs'] as $name => $spec) { ProductSpec::updateOrCreate( ['product_id' => $product->id, 'name' => $name], ['value' => $spec['value'], 'unit' => $spec['unit'], 'source' => $source] ); } } $product->save(); } } Як працює дедуплікація?
Приклад налаштування пріоритетів джерел: масив sourcePriority визначає, яке джерело вважається основним. Чим вище число, тим пріоритетніше. При конфлікті переможе більш пріоритетне.
private array $sourcePriority = [ 'manufacturer_direct' => 100, 'ekatalog' => 80, 'yandex_market' => 70, 'ozon' => 60, 'price_ru' => 50, ]; Дедуплікація виконується за зовнішнім ID (GTIN, артикул). Якщо два джерела надають один і той самий товар, дані беруться з більш пріоритетного. Це виключає дублі та конфлікти.
Строки реалізації
- Один адаптер (YML від Яндекс.Маркет), збагачення порожніх полів — 2 дні
- Мультиагрегаторна структура + пріоритети + ринкові ціни — +2 дні
- OZON/WB API, динамічне ціноутворення на основі ринку — +2–3 дні
Що входить у роботу
- Розробка та налаштування адаптерів під кожне джерело
- Нормалізація та дедуплікація даних
- Збагачення порожніх полів (описи, характеристики, зображення)
- Підготовка документації щодо структури даних та процесу оновлення
- Передача доступів до системи (особистий кабінет, FTP, API-ключі)
- Навчання контент-менеджерів роботі з імпортом
- Технічна підтримка протягом місяця після запуску
Підсумковий строк залежить від кількості адаптерів та складності нормалізації. Зв'яжіться з нами для безкоштовної оцінки вашого проєкту. Замовте імпорт товарів і отримайте каталог з повними даними за 2 дні.







