Реалізація експорту товарів у YML-фід
YML (Yandex Market Language) — XML-формат, який Яндекс.Маркет вимагає для завантаження каталогу. Правильно сформований фід забезпечує індексацію без помилок, коректне відображення офертів та допуск до платних розміщень. Неправильний фід — це відкинені товари, штрафні бали магазину та вивалювання з пошуку.
Структура YML та вимоги Яндекс.Маркету
Формат описаний у офіційній документації. Мінімальна структура:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE yml_catalog SYSTEM "shops.dtd">
<yml_catalog date="2024-01-15 10:00">
<shop>
<name>Мій магазин</name>
<company>ТОВ Компанія</company>
<url>https://example.com</url>
<currencies>
<currency id="RUR" rate="1"/>
</currencies>
<categories>
<category id="10">Електроніка</category>
<category id="11" parentId="10">Смартфони</category>
</categories>
<offers>
<offer id="12345" available="true">
<url>https://example.com/product/12345</url>
<price>29990</price>
<currencyId>RUR</currencyId>
<categoryId>11</categoryId>
<picture>https://example.com/img/12345.jpg</picture>
<name>Смартфон Samsung Galaxy S24</name>
<vendor>Samsung</vendor>
<description>...</description>
<param name="Колір">Чорний</param>
<param name="Пам'ять">256 GB</param>
</offer>
</offers>
</shop>
</yml_catalog>
Критичні поля для допуску: url, price, currencyId, categoryId, name. Поле available впливає на відображення — товари з false виключаються з пошуку, але зберігаються в історії цін.
Архітектура генератора фіду
Для невеликих каталогів (до 10 000 товарів) фід можна генерувати на льоту. Для крупних — тільки через фонову задачу з записом на диск або в CDN.
Схема роботи:
Cron/Queue → Builder → XML Writer → Storage (disk/S3) → Public URL
Реалізація на PHP (Laravel):
class YmlFeedBuilder
{
public function build(string $outputPath): void
{
$writer = new XMLWriter();
$writer->openUri($outputPath);
$writer->startDocument('1.0', 'UTF-8');
$writer->writeDtd('yml_catalog', null, 'shops.dtd');
$writer->startElement('yml_catalog');
$writer->writeAttribute('date', now()->format('Y-m-d H:i'));
$this->writeShopInfo($writer);
$this->writeCurrencies($writer);
$this->writeCategories($writer);
$this->writeOffers($writer);
$writer->endElement();
$writer->endDocument();
$writer->flush();
}
private function writeOffers(XMLWriter $writer): void
{
$writer->startElement('offers');
Product::with(['category', 'attributes', 'images'])
->where('active', true)
->where('price', '>', 0)
->chunk(500, function ($products) use ($writer) {
foreach ($products as $product) {
$this->writeOffer($writer, $product);
}
});
$writer->endElement();
}
}
Використання chunk() обов'язково — спроба завантажити 50 000+ товарів одним запитом гарантовано дасть OOM.
Типи офертів
Яндекс.Маркет розрізняє кілька типів, кожен з обов'язковими полями:
| Тип | Застосування | Обов'язкові додаткові поля |
|---|---|---|
vendor.model |
Електроніка, техніка | vendor, model |
book |
Книги | author, isbn |
audiobook |
Аудіокниги | author, publisher |
medicine |
Ліки | vendor, registration-number |
artist.title |
Музика, кіно | artist, title |
tour |
Тури | worldRegion, country |
| За замовчуванням | Все інше | — |
Для більшості інтернет-магазинів працює vendor.model або тип за замовчуванням.
Валідація фіду
Перед відправкою в Яндекс.Маркет фід потрібно перевіряти локально. Використовуйте Feed Validator або схему shops.dtd.
Власна валідація на етапі збірки:
class YmlFeedValidator
{
public function validate(string $filePath): array
{
$errors = [];
$dom = new DOMDocument();
if (!$dom->load($filePath)) {
return ['XML parse error'];
}
$offers = $dom->getElementsByTagName('offer');
foreach ($offers as $offer) {
$id = $offer->getAttribute('id');
if (!$offer->getElementsByTagName('price')->length) {
$errors[] = "Офер {$id}: відсутня ціна";
}
if (!$offer->getElementsByTagName('url')->length) {
$errors[] = "Офер {$id}: відсутнє посилання";
}
// Перевірка довжини description (Яндекс обрізає після 3000 символів)
$desc = $offer->getElementsByTagName('description')->item(0);
if ($desc && mb_strlen($desc->textContent) > 3000) {
$errors[] = "Офер {$id}: опис занадто довгий";
}
}
return $errors;
}
}
Розписання оновлення
Частота залежить від динаміки каталогу:
- Ціни та остатки — змінюються часто: рекомендується регенерація кожні 1–4 години
- Атрибути та описи — змінюються повільно: достатньо 1 раз на день
- Нові товари — негайно або при наступному годинному циклі
Приклад розписання в Laravel (app/Console/Kernel.php):
$schedule->job(new GenerateYmlFeedJob)->everyFourHours();
Оптимізація розміру фіду
Яндекс.Маркет рекомендує не перевищувати 500 МБ на фід. Якщо каталог більший — розбивайте по категоріям та реєструйте кілька фідів у особистому кабінеті.
Зниження розміру без втрати якості:
- Виключати товари без ціни (
WHERE price > 0) - Виключати неактивні категорії
- Обрізати
descriptionдо 1000–1500 символів - Стискати фід gzip (
Content-Encoding: gzip) — Яндекс підтримує
// Стиснення готового фіду
$source = storage_path('app/public/feed.xml');
$compressed = storage_path('app/public/feed.xml.gz');
$fp = gzopen($compressed, 'w9');
gzwrite($fp, file_get_contents($source));
gzclose($fp);
Терміни реалізації
- Базовий генератор з типом за замовчуванням: 1–2 дні
- Додавання типізованих офертів (
vendor.model): +0.5 дня - Валідатор + логування помилок: +0.5 дня
- Налаштування cron + публічний URL: +0.5 дня
Загалом типовий проект: 2–3 робочих дні.







