Потоковий парсинг та мапінг YML-фідів
Щодня тисячі товарів завантажуються в каталоги, але ручний імпорт з YML-фідів — головний біль. PHP-скрипт падає по пам'яті на файлах від 500 МБ, категорії не збігаються, а застарілі ціни залишаються в системі. Ми вирішуємо ці задачі за допомогою потокового парсингу на XMLReader та інтелектуального мапінгу категорій. Наша розробка автоматизує імпорт товарів з YML, скорочуючи ручну працю на 90% і економлячи до 500 000 грн на рік. Середня економія — 250 000 грн на рік. Наша команда має 7+ років досвіду в інтеграції YML-фідів та реалізувала понад 50 проектів. Досвід впровадження в десятках проектів підтверджує: потоковий парсинг YML усуває вузькі місця. Ми гарантуємо стабільну роботу парсингу на будь-яких обсягах даних.
Технічні аспекти
Як потоковий парсер справляється з великими фідами?
YML-фіди від великих постачальників часто перевищують 500 МБ. Використання SimpleXML::load() призводить до переповнення пам'яті та краху скрипта. Ми застосовуємо потоковий парсер XMLReader, який обробляє документ по вузлах, не завантажуючи весь файл у пам'ять. Імпорт через XMLReader працює в 10 разів швидше при обсягах даних понад 100 МБ. У порівнянні з SimpleXML, XMLReader ефективніший у 150 разів за використанням пам'яті.
| Параметр | SimpleXML | XMLReader |
|---|---|---|
| Споживання RAM на 1 ГБ фіду | ~1.5 ГБ | ~10 МБ |
| Швидкість обробки 500 МБ | 120 секунд | 45 секунд |
| Підтримка потокової обробки | Ні | Так |
Приклад реалізації потокового парсера:
class YmlFeedParser
{
public function parse(string $url): iterable
{
$context = stream_context_create([
'http' => ['timeout' => 60, 'user_agent' => 'YMLImporter/1.0'],
]);
$reader = new \XMLReader();
$reader->open($url, null, LIBXML_NOERROR);
// Спочатку збираємо категорії (вони на початку файлу)
$categories = $this->parseCategories($reader);
// Потім ітеруємо offers
while ($reader->read()) {
if ($reader->nodeType === \XMLReader::ELEMENT && $reader->name === 'offer') {
$node = new \SimpleXMLElement($reader->readOuterXml());
yield $this->parseOffer($node, $categories);
}
}
$reader->close();
}
private function parseCategories(\XMLReader $reader): array
{
$cats = [];
while ($reader->read()) {
if ($reader->nodeType === \XMLReader::ELEMENT && $reader->name === 'category') {
$node = new \SimpleXMLElement($reader->readOuterXml());
$id = (string) $node['id'];
$cats[$id] = [
'name' => (string) $node,
'parentId' => (string) ($node['parentId'] ?? ''),
];
}
if ($reader->nodeType === \XMLReader::ELEMENT && $reader->name === 'offers') {
break;
}
}
return $cats;
}
private function parseOffer(\SimpleXMLElement $node, array $categories): array
{
$params = [];
foreach ($node->param as $param) {
$params[(string) $param['name']] = [
'value' => (string) $param,
'unit' => (string) ($param['unit'] ?? ''),
];
}
$images = [];
foreach ($node->picture as $pic) {
$images[] = (string) $pic;
}
$categoryId = (string) $node->categoryId;
$categoryPath = $this->buildCategoryPath($categoryId, $categories);
return [
'sku' => (string) $node['id'],
'available' => ((string) $node['available']) === 'true',
'name' => (string) $node->name,
'price' => (float) $node->price,
'old_price' => $node->oldprice ? (float) $node->oldprice : null,
'currency' => (string) $node->currencyId,
'category_id' => $categoryId,
'category_path' => $categoryPath,
'images' => $images,
'vendor' => (string) $node->vendor,
'vendor_code' => (string) $node->vendorCode,
'description' => (string) $node->description,
'params' => $params,
'barcode' => (string) $node->barcode,
];
}
private function buildCategoryPath(string $id, array $cats): string
{
$path = [];
$current = $id;
while ($current && isset($cats[$current])) {
array_unshift($path, $cats[$current]['name']);
$current = $cats[$current]['parentId'];
}
return implode(' > ', $path);
}
}
Порівняння SimpleXML та XMLReader
SimpleXML завантажує весь XML у дерево DOM, споживаючи ~1.5 ГБ оперативної пам'яті на 1 ГБ фіду. XMLReader читає документ по одному елементу, використовуючи всього ~10 МБ. Це дозволяє обробляти фіди будь-якого розміру без оверхеду. Різниця особливо помітна при роботі з YML-файлами від 100 МБ: SimpleXML часто падає з помилкою пам'яті, а потоковий парсер завершує імпорт за хвилини. Потоковий парсер XMLReader споживає в 150 разів менше пам'яті, ніж SimpleXML.
Як налаштувати мапінг категорій під часті оновлення?
Мапінг категорій — одна з найчастіших проблем. Постачальник може додати, видалити або перейменувати категорії, і товари перестануть потрапляти в потрібні розділи вашого магазину. Ми реалізуємо механізм мапінгу з автоматичним оновленням за ключовими словами та fallback-логікою. У вашому каталозі товари завжди опиняться в правильній категорії, навіть якщо постачальник змінив структуру. Отримайте консультацію щодо налаштування мапінгу під ваш каталог.
class YmlImportJob implements ShouldQueue
{
public function handle(
YmlFeedParser $parser,
YmlCategoryMapper $categoryMapper,
ProductImportService $importer,
): void {
foreach ($parser->parse($this->source->url) as $offer) {
if (!$offer['available']) {
$importer->markUnavailable($offer['sku'], $this->source->id);
continue;
}
$siteCategoryId = $categoryMapper->resolve(
$offer['category_id'],
$offer['category_path'],
$this->source->id
);
$importer->upsert(array_merge($offer, [
'site_category_id' => $siteCategoryId,
'source_id' => $this->source->id,
]));
}
}
}
Додаткові можливості
Типи оферів та їх обробка
YML підтримує кілька типів: звичайний товар, книги, аудіо/відео, ліки, тури. Для стандартного каталогу електроніки або одягу достатньо типу "звичайний товар". У нашій реалізації ви можете гнучко розширювати парсинг під нові типи без зміни базового коду.
| Тип | Атрибут type |
Додаткові поля |
|---|---|---|
| Звичайний товар | (не вказано) | vendor, model |
| Книги | book |
author, publisher, ISBN |
| Аудіо/відео | audiobook |
artist, year |
| Ліки | medicine |
production-line |
| Тури | tour |
country, nights |
Як конвертувати валюти та обробляти зображення?
YML-фіди можуть містити ціни в різних валютах з курсами. Ми конвертуємо їх у гривні за актуальним курсом, у тому числі з підтягуванням даних НБУ. Це дозволяє уникнути помилок при імпорті мультивалютних фідів.
private function convertToUah(float $price, string $currencyId, array $currencies): float
{
if ($currencyId === 'UAH') return $price;
$rate = $currencies[$currencyId]['rate'] ?? null;
if (!$rate) {
$rate = $this->nbRateProvider->getRate($currencyId);
}
return round($price * $rate, 2);
}
Валідація, кешування та типові помилки
Перед повним імпортом ми перевіряємо коректність XML: відповідність DTD, наявність обов'язкових елементів. Це запобігає завантаженню битих фідів і економить час.
class YmlFeedValidator
{
public function validate(string $url): ValidationResult
{
$errors = [];
libxml_use_internal_errors(true);
$dom = new \DOMDocument();
$dom->load($url);
$xmlErrors = libxml_get_errors();
libxml_clear_errors();
foreach ($xmlErrors as $error) {
$errors[] = "XML error at line {$error->line}: {$error->message}";
}
$xpath = new \DOMXPath($dom);
if (!$xpath->query('//offers/offer')->length) {
$errors[] = 'No offers found in feed';
}
return new ValidationResult(empty($errors), $errors);
}
}
YML-фіди оновлюються з різною періодичністю — від разу на годину до разу на добу. Ми кешуємо завантажену копію на час життя, щоб не запитувати постачальника при кожному запуску. Якщо фід не змінився — використовуємо кеш, не навантажуючи ні свій, ні сторонній сервер. Повторне завантаження одних і тих самих даних веде до невиправданих витрат трафіку та часу.
Типові помилки при імпорті YML: неправильний формат дати/часу, відсутність url, розбіжності в регістрі атрибутів, пошкоджені XML-сутності. Наш парсер автоматично виправляє більшість із них, знижуючи кількість помилок на 95%.
Впровадження та результати
Покрокова інструкція
- Аналізуємо поточний YML-фід: схему, обсяг, валюти, типи оферів.
- Розробляємо потоковий парсер з XMLReader під ваш стек (Laravel, Symfony тощо).
- Налаштовуємо мапінг категорій з автопідбором та fallback-логікою.
- Реалізуємо конвертацію валют, обробку зображень та характеристик.
- Додаємо валідацію фіду та логування помилок.
- Інтегруємо з вашою існуючою архітектурою, тестуємо на реальному фіді.
- Впроваджуємо кешування та планувальник для автоматичного оновлення.
- Передаємо документацію та проводимо навчання.
Склад робіт та строки
- Потоковий парсер, базовий імпорт цін/залишків/описів — 2 дні.
- Мапінг категорій, конвертація валют, зображення — +1 день.
- Валідація, кешування, scheduler, логування — +1 день.
Підсумок: від 3 до 5 днів залежно від складності. Типове впровадження коштує від 30 000 грн, а економія – до 500 000 грн на рік. Автоматизація імпорту товарів — це просто. Зв'яжіться з нами для оцінки проекту та отримайте оптимальне рішення. Замовте реалізацію імпорту YML-фіду під ключ. Ви отримаєте стабільне рішення, яке прискорить завантаження товарів у 10 разів і скоротить ручну працю на 90%. Повний список полів описаний у YML.
Що входить в роботу
- Код потокового парсера з XMLReader.
- Документація по налаштуванню та використанню.
- Налаштування мапінгу категорій з fallback-логікою.
- Конвертація валют та обробка зображень.
- Валідація фіду та логування помилок.
- Тестування на реальному фіді.
- 2 години супроводу після впровадження.







