Почему ломаются фиды для Rozetka: изнанка XML, маппинга и скрытых атрибутов
В ноябре 2022 года я просидел до четырех утра, тупо глядя в diff двух XML-файлов размером по 70 мегабайт каждый. У клиента — магазин сантехники и отопления, 8 400 активных позиций на связке OpenCart и 1С. Полгода выгрузка каталога на маркетплейс работала как часы. В четверг в 19:30 Rozetka молча перевела 6 100 товаров в статус «Заблокировано модератором», а телефон менеджера начал разрываться от уведомлений о сорванных заказах.
Причина? В описании польских полотенцесушителей поставщик вставил неразрывный пробел с кодом 0x00A0 и неэкранированный амперсанд в названии серии: «Comfort & Heat». Парсер маркетплейса подавился ровно на сотом товаре, выплюнул фатальную ошибку парсинга и сбросил остатки по всем последующим узлам. Никаких внятных логов в личном кабинете — просто плашка «Ошибка структуры файла».
Большинство владельцев магазинов уверены: достаточно поставить типовой модуль за 30 долларов, который выплевывает YML-файл по ссылке, вставить ссылку в кабинет мерчанта — и готово. Это иллюзия. До первой смены требований площадки или первой корявой строчки из базы поставщика.
XML-валидация: почему ломаются стандартные плагины
Стандартный экспорт из CMS генерирует XML «на лету» прямо в поток вывода PHP. Пока у вас 300 товаров, скрипт справляется за секунду. Когда товаров становится пять тысяч, генерация начинает упираться в memory_limit или max_execution_time веб-сервера. Если бот площадки стучится за фидом, а сервер отдает наполовину сформированное дерево из-за таймаута на 30-й секунде, маркетплейс считает весь файл битым.
Вторая беда — «мусорные» спецсимволы. Достаточно менеджеру скопировать текст из Excel или переписки в Skype, и в базу улетают скрытые управляющие символы (ASCII 0–31), невалидные закрывающие теги и кривой UTF-8. Если модуль выгрузки не упаковывает описание в <![CDATA[ ... ]]> или не прогоняет строки через жесткую санитизацию регулярками, один кривой тройник с резьбой ломает весь фид.
Мы давно перестали отдавать динамические фиды напрямую из базы. Только фоновая генерация во временный файл, валидация по схеме XSD, и только при нулевом коде ошибок — атомарная подмена старого файла через rename(). Бот должен получать либо идеальный файл за 200 миллисекунд, либо HTTP 503, но никогда — огрызок данных.
Ад категорийного маппинга: дерево против дерева
Ваша внутренняя структура каталога никого не интересует. У вас может быть идеальное, выстраданное годами дерево: «Климатическая техника > Отопление > Радиаторы > Биметаллические». У Rozetka своя жесткая иерархия, завязанная на их внутренние ID категорий.
Если сопоставить категорию неправильно, карточка улетит в чужой раздел. В лучшем случае ее отклонит ручная модерация через трое суток ожидания. В худшем — товар появится в разделе, где его никто не найдет, или алгоритм заблокирует карточку за спам. Ручной маппинг в админке на 200 подкатегорий — это верный способ сойти с ума, потому что раз в квартал маркетплейсы разделяют категории, объединяют их или меняют родительские ветки.
Рабочая схема требует слоя изоляции. Мы строим промежуточные словари сопоставления. Если категория магазина не смаплена на актуальный ID маркетплейса, эти позиции просто исключаются из фида, а администратор получает алерт в Telegram. Лучше временно не выгрузить 20 новинок, чем отправить мусор и нарваться на понижение рейтинга мерчанта.
Дрейф атрибутов и обязательные параметры
Самая коварная проблема — это параметры товаров (теги <param>). В один прекрасный день отдел контента маркетплейса решает, что в категории «Смесители» атрибут «Материал корпуса» теперь строго обязателен, причем принимать его можно только из предустановленного списка значений. Была у вас «Латунь сантехническая»? Теперь требуется строго «Латунь».
Что происходит с товарами без этого параметра? Они просто выпадают из выдачи при фильтрации пользователями. Клиент видит, что выгрузка каталога на маркетплейс работает, ошибок нет, позиции «активны», а трафик и заказы падают в ноль. Потому что 95% покупателей ищут товар через боковые фильтры, где ваших карточек физически нет.
Когда выстраивается надежная автоматическая выгрузка товаров, правила валидации атрибутов должны жить на стороне коннектора. Система обязана уметь трансформировать значения: отрезать единицы измерения («50 см» превращать в «50» с отдельным параметром размерности), приводить синонимы к единому словарю и подставлять дефолтные значения там, где они допустимы.
Как должна выглядеть стабильная архитектура
За годы возни с API и фидами я вывел три базовых правила, без которых любая интеграция превращается в пороховую бочку:
Первое: полная изоляция процесса выгрузки. Генерация XML или пуш через API не должны зависеть от фронтенда магазина. Это отдельный демон или консольная команда (CLI), работающая по крону с лимитом памяти от 512 МБ.
Второе: дифференциальные апдейты. Гонять каждый час файл на 15 000 позиций ради обновления остатков по пяти товарам — безумие. Полноценная синхронизация товаров маркетплейс должна разделять тяжелые контентные данные (картинки, описания, характеристики), которые меняются редко, и легковесные триггеры цен и остатков.
Третье: валидатор перед отправкой. Если выходной XML не проходит локальный парсинг через XMLReader или не отвечает схеме валидации Rozetka, он не должен уходить на сервер площадки. Точка.
Если вы устали вручную пересобирать прайсы после каждого сбоя и вам нужен отказоустойчивый автофид rozetka, который не падает от кривых символов и сам следит за изменениями категорий, мы в GuardLabs решаем эту задачу под ключ. Наша автовыгрузка каталога на маркетплейсы (Rozetka и другие) из CMS магазина убирает головную боль с валидацией, приводит атрибуты к стандартам площадок и держит остатки актуальными без ручного вмешательства.