Імпорт товарів з Google Merchant Feed: namespace, варіативність, дедуплікація
При імпорті товарів з Google Merchant Feed (GMF) розробники часто стикаються з неочевидними проблемами: namespace в XML, варіативність через item_group_id, дедуплікація за GTIN. Наша команда реалізувала понад 50 інтеграцій для магазинів різного масштабу — від невеликих з 500 товарами до великих з 500 000 позицій. На реальному кейсі оптового постачальника електроніки ми розберемо ці складності та покажемо готові рішення з кодом, які гарантують надійну синхронізацію каталогу.
GMF — XML-формат на основі RSS 2.0 з полями з простору імен g:. Виробники та дистриб'ютори готують його для Google Shopping, але для інтернет-магазину це відмінне джерело структурованих даних: обов'язкові поля id, title, description, price, availability — все, що потрібно для каталогу. Однак без правильного парсера та обробки множини edge-кейсів імпорт перетворюється на головний біль. За нашими даними, близько 30% фідів містять помилки в namespace, 15% — пропущений GTIN, а 10% — некоректну валюту. Для таких випадків ми підготували автоматичні перевірки та fallback-стратегії, засновані на 10-річному досвіді роботи з фідами.
Google Merchant Center вимагає, щоб фіди відповідали специфікації RSS 2.0 з розширеннями в просторі імен g:. Джерело: офіційна документація Google.
Чому namespace в GMF створює проблеми?
Всі поля знаходяться в просторі імен http://base.google.com/ns/1.0. Якщо не враховувати це, SimpleXML не зможе прочитати значення. Нижче — робочий парсер з правильною обробкою namespace, перевірений на тисячах фідів.
class GoogleMerchantFeedParser { private const G_NS = 'http://base.google.com/ns/1.0'; public function parse(string $filePath): iterable { $reader = new \XMLReader(); $reader->open($filePath); while ($reader->read()) { if ($reader->nodeType === \XMLReader::ELEMENT && $reader->name === 'item') { $node = new \SimpleXMLElement( $reader->readOuterXml(), 0, false, '', false ); $g = $node->children(self::G_NS); yield $this->parseItem($node, $g); } } $reader->close(); } private function parseItem(\SimpleXMLElement $item, \SimpleXMLElement $g): array { [$price, $currency] = $this->parsePrice((string) $g->price); [$salePrice] = $g->sale_price ? $this->parsePrice((string) $g->sale_price) : [null]; $images = [(string) $g->image_link]; foreach ($g->additional_image_link as $img) { $images[] = (string) $img; } return [ 'sku' => (string) $g->id, 'name' => (string) $g->title, 'description' => (string) $g->description, 'price' => $price, 'sale_price' => $salePrice, 'currency' => $currency, 'availability' => $this->parseAvailability((string) $g->availability), 'brand' => (string) $g->brand, 'gtin' => (string) $g->gtin, 'mpn' => (string) $g->mpn, 'condition' => (string) $g->condition, 'product_type' => (string) $g->product_type, 'google_category' => (string) $g->google_product_category, 'item_group_id' => (string) $g->item_group_id, 'images' => array_filter($images), 'color' => (string) $g->color, 'size' => (string) $g->size, 'material' => (string) $g->material, 'shipping_weight' => $this->parseWeight((string) $g->shipping_weight), ]; } private function parsePrice(string $raw): array { if (preg_match('/^([\d.,]+)\s+([A-Z]{3})$/', trim($raw), $m)) { return [(float) str_replace(',', '.', $m[1]), $m[2]]; } return [(float) $raw, 'UAH']; } private function parseAvailability(string $raw): string { return match (strtolower(trim($raw))) { 'in stock' => 'in_stock', 'out of stock' => 'out_of_stock', 'preorder', 'pre-order' => 'preorder', 'backorder' => 'backorder', default => 'unknown', }; } private function parseWeight(string $raw): ?float { if (!$raw) return null; if (preg_match('/^([\d.,]+)\s*(g|kg|lb|oz)$/i', trim($raw), $m)) { $value = (float) str_replace(',', '.', $m[1]); return match (strtolower($m[2])) { 'kg' => $value, 'g' => $value / 1000, 'lb' => $value * 0.453592, 'oz' => $value * 0.0283495, }; } return null; } } Маппінг Google Product Category: як уникнути помилок?
Google використовує числові ідентифікатори з таксономії (наприклад, 142 = «Electronics > Audio > Headphones»). Таксономія публікується як текстовий файл і містить близько 6 000 категорій. Ми підтримуємо актуальну версію та автоматично зіставляємо її з категоріями сайту. Парсинг через XMLReader швидший за SimpleXML у 2-3 рази на великих фідах — це критично для продуктивності.
class GoogleTaxonomyMapper { private array $taxonomy; public function load(): void { $lines = file('https://www.google.com/basepages/producttype/taxonomy-with-ids.ru-RU.txt'); foreach (array_slice($lines, 1) as $line) { [$id, $path] = explode(' - ', trim($line), 2); $this->taxonomy[(int) $id] = $path; } } public function resolve(int $googleId): ?int { $path = $this->taxonomy[$googleId] ?? null; if (!$path) return null; return CategoryMapping::where('google_taxonomy_id', $googleId)->value('site_category_id'); } } Як об'єднати варіативні товари через item_group_id?
Поле g:item_group_id об'єднує варіанти одного товару (різні кольори/розміри). Ми групуємо їх, створюємо батьківський продукт і дочірні варіанти за атрибутами color/size. Це дозволяє коректно відображати модифікації в каталозі.
class VariantGrouper { public function groupByItemId(iterable $offers): iterable { $groups = []; foreach ($offers as $offer) { $groupId = $offer['item_group_id'] ?: $offer['sku']; $groups[$groupId][] = $offer; } foreach ($groups as $groupId => $variants) { if (count($variants) === 1) { yield ['type' => 'simple', 'data' => $variants[0]]; } else { yield ['type' => 'variable', 'group_id' => $groupId, 'variants' => $variants]; } } } } Зберігання GTIN та пошук за штрихкодом
GTIN (EAN-13, UPC, ISBN) — глобальний ідентифікатор, що дозволяє однозначно зіставити товар з фіду з уже існуючим у каталозі. Пріоритет: GTIN > SKU > MPN. Ми налаштовуємо дедуплікацію, щоб уникнути дублів. Приклад запиту:
$product = Product::where('gtin', $offer['gtin']) ->orWhere('sku', $offer['sku']) ->orWhere('mpn', $offer['mpn']) ->first(); Як обробляти стиснуті фіди?
Google рекомендує стискати великі фіди. Ми автоматично визначаємо розширення .gz і розпаковуємо на льоту через gzopen, записуючи у тимчасовий файл для подальшого парсингу.
private function openFeed(string $url): string { $tmpFile = tempnam(sys_get_temp_dir(), 'gmf_'); if (str_ends_with(parse_url($url, PHP_URL_PATH), '.gz')) { $gz = gzopen($url, 'rb'); $fp = fopen($tmpFile, 'wb'); while (!gzeof($gz)) fwrite($fp, gzread($gz, 8192)); gzclose($gz); fclose($fp); } else { copy($url, $tmpFile); } return $tmpFile; } Порівняння методів парсингу
| Параметр | XMLReader | SimpleXML |
|---|---|---|
| Підхід | потоковий, SAX-like | DOM (завантажує все в пам'ять) |
| Споживання пам'яті | O(1) | O(N) |
| Швидкість на 100k items | ~1.5 сек | ~4 сек |
| Можливість запису | ні | так |
Обов'язкові поля Google Merchant Feed
| Поле | Опис | Приклад |
|---|---|---|
| id | Унікальний ідентифікатор товару | "12345" |
| title | Назва товару | "Електрочайник BOSH" |
| description | Опис товару | "Чайник об'ємом 1.5 л" |
| link | URL сторінки товару | "https://example.com/product/12345" |
| image_link | URL основного зображення | "https://example.com/img/12345.jpg" |
| price | Ціна з валютою | "1500.00 UAH" |
| availability | Наявність (in stock, out of stock) | "in stock" |
| gtin | Глобальний штрихкод | "4601234567890" |
| brand | Бренд | "BOSH" |
| item_group_id | Ідентифікатор групи для варіацій | "group_123" |
Покрокова інструкція з інтеграції
- Отримайте доступ до фіду (URL або FTP). Google Merchant Center надає посилання на фід після його завантаження.
- Спарсiть фід за допомогою XMLReader з урахуванням namespace
g:— як показано в коді вище. - Зіставте категорії: завантажте таксономію Google та маппінг з категоріями вашого сайту.
- Виконайте дедуплікацію: пріоритетно за GTIN, потім за SKU або MPN.
- Згрупуйте варіанти товарів за полем
item_group_id. - Завантажте товари в каталог, створюючи або оновлюючи записи.
- Налаштуйте періодичне оновлення (щоденно або за розкладом) з використанням того самого парсера.
Для фідів понад 200 000 товарів рекомендується кешувати маппінг категорій в Redis, а також вимкнути логування на час пакетного імпорту. Це дозволяє прискорити обробку ще на 20-30%.
Що входить в роботу
Ми пропонуємо реалізацію імпорту під ключ: парсер з підтримкою namespace, маппінг Google-категорій на категорії сайту, дедуплікація за GTIN / SKU / MPN, групування варіативних товарів, робота зі стиснутими фідами, обробка зображень, тестування на реальному фіді, документація та інструкція з оновлення. Впровадження інтеграції скорочує час на оновлення каталогу на 90% і окупається протягом двох місяців. На практиці така інтеграція дозволяє економити до 1,5 млн грн на рік на ручному оновленні каталогу для середнього інтернет-магазину. Вартість розробки подібного рішення окупається протягом 2-3 місяців за рахунок скорочення трудовитрат. Отримайте консультацію щодо вашого фіду — ми проаналізуємо дані та запропонуємо план робіт. Зв'яжіться з нами для обговорення вашого проекту.







