יצירת פיד מוצרים עבור Yandex.Market (YML)
שים לב: כאשר פיד נחסם עקב תג oldprice שגוי, הקמפיין מאבד עד 70% מהתנועה ביום. נתקלנו במצב זה עם לקוח שהיה לו קטלוג של 15,000 פריטים: Yandex החזיר את השגיאה "המחיר באתר שונה מהפיד ביותר מ-1%" עבור מחצית מההצעות. הסיבה הייתה עדכוני מחירים לא מסונכרנים ב-CRM ובפיד. לפי העזרה של Yandex.Market, YML (Yandex Market Language) הוא ניב XML קפדני, וכל שגיאה עוצרת את כל הקמפיין.
מדוע שגיאה אחת שוברת את כל הקמפיין?
Yandex בודק כל פריט: מחיר באתר ובפיד, זמינות תמונה, אורך תיאור. שגיאה בהצעה אחת עוצרת את כל הקמפיין. לדוגמה, הפרש מחירים של יותר מ-1% — המוצר לא נכנס לאינדקס. או תמונה שלא נפתחת — ההצעה מוחרגת.
באיזו תדירות כדאי לעדכן את הפיד?
התדירות המינימלית היא פעם אחת בכל 24 שעות, אך אנו ממליצים לעדכן כל שעה אם המחירים משתנים לעתים קרובות. עבור התאמות מחירים מהירות, מחובר Price API, שמעדכן רק מחירים ומלאי ללא רגנרציה מלאה. הפיד YML קל יותר להגדרה אך פחות גמיש. בממוצע, לקוחות שעוברים לעדכונים שעתיים רואים ירידה של 20% בהצעות מוחרגות.
פורמטים להעברת נתונים ב-Yandex.Market
Yandex תומך בשתי שיטות:
- פיד YML — קובץ דרך URL ש-Yandex מוריד בלוח זמנים (מינימום פעם ב-24 שעות, מקסימום פעם בשעה)
- Price API — ממשק תכנותי לעדכון רק מחירים וזמינות ללא רגנרציה מלאה של הפיד
עבור רוב החנויות, פיד YML מספיק. Price API מחובר בנוסף אם המחירים משתנים מספר פעמים ביום.
השוואה בין YML ל-Price API
| מאפיין | פיד YML | Price API |
|---|---|---|
| תדירות עדכון | 1–24 שעות | מיידי |
| נפח נתונים | כל הקטלוג | רק מחירים/מלאי |
| מורכבות הגדרה | נמוכה | בינונית |
| סיכון לשגיאות | גבוה יותר (אימות פיד מלא) | נמוך יותר (רק שדות בודדים) |
| מהירות שינוי | פי 24 איטי יותר מ-Price API לעדכוני מחירים | עדכונים מיידיים |
מבנה מסמך YML
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE yml_catalog SYSTEM "shops.dtd">
<yml_catalog date="2024-01-01 14: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">Apple iPhone</category>
</categories>
<delivery-options>
<option cost="299" days="1"/>
</delivery-options>
<offers>
<offer id="12345" available="true">
<url>https://example.com/products/iphone-15</url>
<price>89990</price>
<oldprice>99990</oldprice>
<currencyId>RUR</currencyId>
<categoryId>11</categoryId>
<picture>https://cdn.example.com/iphone-15-1.jpg</picture>
<picture>https://cdn.example.com/iphone-15-2.jpg</picture>
<name>Смартфон Apple iPhone 15 128GB черный</name>
<vendor>Apple</vendor>
<vendorCode>MTP03LL/A</vendorCode>
<barcode>0194253401353</barcode>
<description>Смартфон Apple iPhone 15 с процессором A16...</description>
<param name="Цвет">Чёрный</param>
<param name="Встроенная память" unit="ГБ">128</param>
<param name="Операционная система">iOS</param>
</offer>
</offers>
</shop>
</yml_catalog> מאפיינים של סוגי הצעות
Yandex מבחין בין מספר סוגים של הצעות מוצר:
| סוג | מתי להשתמש | שדות נוספים מרכזיים |
|---|---|---|
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE yml_catalog SYSTEM "shops.dtd"> <yml_catalog date="2024-01-01 14: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">Apple iPhone</category> </categories> <delivery-options> <option cost="299" days="1"/> </delivery-options> <offers> <offer id="12345" available="true"> <url>https://example.com/products/iphone-15</url> <price>89990</price> <oldprice>99990</oldprice> <currencyId>RUR</currencyId> <categoryId>11</categoryId> <picture>https://cdn.example.com/iphone-15-1.jpg</picture> <picture>https://cdn.example.com/iphone-15-2.jpg</picture> <name>Смартфон Apple iPhone 15 128GB черный</name> <vendor>Apple</vendor> <vendorCode>MTP03LL/A</vendorCode> <barcode>0194253401353</barcode> <description>Смартфон Apple iPhone 15 с процессором A16...</description> <param name="Цвет">Чёрный</param> <param name="Встроенная память" unit="ГБ">128</param> <param name="Операционная система">iOS</param> </offer> </offers> </shop> </yml_catalog> |
אלקטרוניקה, מוצרי חשמל ביתיים | vendor.model, typePrefix, vendor |
model |
ספרים | book, author, publisher, ISBN |
year |
ספרי שמע | audiobook, author, publisher |
performed-by |
מוזיקה, וידאו, משחקים | artist.title, artist, title, year |
media |
טיולים | tour, worldRegion, hotel-stars, room |
dataTour |
כרטיסים | event-ticket, place, hall, date |
| פשוט | כל השאר | רק שדות בסיסיים |
כיצד לאוטומציה את יצירת הפיד?
אנו משתמשים ב-PHP עם is-premiere — הוא כותב ישירות לקובץ, ומונע החזקת כל ה-XML בזיכרון. זה קריטי עבור קטלוגים עם 100,000+ פריטים.
class YandexMarketFeedGenerator
{
public function handle(): void
{
$path = storage_path('app/public/feeds/yandex.xml');
$writer = new \XMLWriter();
$writer->openUri($path);
$writer->setIndent(true);
$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->writeShopHeader($writer);
$this->writeCurrencies($writer);
$this->writeCategories($writer);
$this->writeOffers($writer);
$writer->endElement();
$writer->endDocument();
$writer->flush();
}
private function writeOffers(\XMLWriter $w): void
{
$w->startElement('offers');
Product::with(['category', 'brand', 'images', 'attributes'])
->where('is_active', true)
->chunk(500, function ($products) use ($w) {
foreach ($products as $p) {
$w->startElement('offer');
$w->writeAttribute('id', $p->sku);
$w->writeAttribute('available', $p->stock > 0 ? 'true' : 'false');
$w->writeElement('url', route('products.show', $p->slug));
$w->writeElement('price', (string) $p->price);
if ($p->compare_price > $p->price) {
$w->writeElement('oldprice', (string) $p->compare_price);
}
$w->writeElement('currencyId', 'RUR');
$w->writeElement('categoryId', $p->category_id);
$w->writeElement('name', $p->name);
$w->writeElement('vendor', $p->brand?->name ?? '');
$w->writeElement('barcode', $p->barcode ?? '');
foreach ($p->images as $img) {
$w->writeElement('picture', $img->cdn_url);
}
foreach ($p->attributes as $attr) {
$w->startElement('param');
$w->writeAttribute('name', $attr->name);
if ($attr->unit) {
$w->writeAttribute('unit', $attr->unit);
}
$w->text($attr->value);
$w->endElement();
}
$w->endElement();
}
});
$w->endElement();
}
}בפרויקט אמיתי, נתקלנו בקטלוג של 200,000 פריטים, שבו הגנרטור הקודם השתמש ב-XMLWriter וצרך 2 GB של זיכרון RAM. המעבר ל-class YandexMarketFeedGenerator { public function handle(): void { $path = storage_path('app/public/feeds/yandex.xml'); $writer = new \XMLWriter(); $writer->openUri($path); $writer->setIndent(true); $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->writeShopHeader($writer); $this->writeCurrencies($writer); $this->writeCategories($writer); $this->writeOffers($writer); $writer->endElement(); $writer->endDocument(); $writer->flush(); } private function writeOffers(\XMLWriter $w): void { $w->startElement('offers'); Product::with(['category', 'brand', 'images', 'attributes']) ->where('is_active', true) ->chunk(500, function ($products) use ($w) { foreach ($products as $p) { $w->startElement('offer'); $w->writeAttribute('id', $p->sku); $w->writeAttribute('available', $p->stock > 0 ? 'true' : 'false'); $w->writeElement('url', route('products.show', $p->slug)); $w->writeElement('price', (string) $p->price); if ($p->compare_price > $p->price) { $w->writeElement('oldprice', (string) $p->compare_price); } $w->writeElement('currencyId', 'RUR'); $w->writeElement('categoryId', $p->category_id); $w->writeElement('name', $p->name); $w->writeElement('vendor', $p->brand?->name ?? ''); $w->writeElement('barcode', $p->barcode ?? ''); foreach ($p->images as $img) { $w->writeElement('picture', $img->cdn_url); } foreach ($p->attributes as $attr) { $w->startElement('param'); $w->writeAttribute('name', $attr->name); if ($attr->unit) { $w->writeAttribute('unit', $attr->unit); } $w->text($attr->value); $w->endElement(); } $w->endElement(); } }); $w->endElement(); } } הפחית את הצריכה ל-100 MB והאיץ את היצירה מ-40 דקות ל-5. זה הופך את XMLWriter לפי 8 מהיר יותר ופי 20 חסכוני יותר בזיכרון מאשר DOMDocument.
עדכון תצורה
הפיד מתעדכן דרך Laravel Scheduler:
// app/Console/Kernel.php $schedule->job(GenerateYandexFeedJob::class)->hourly()->withoutOverlapping(); קובץ הפיד מוגש דרך נתיב ייעודי או ישירות מ-DOMDocument. אם הקטלוג עולה על 500 MB ב-XML, Yandex ממליץ לפצל את הפיד למספר קבצים ולרשום כל אחד בנפרד בלוח הבקרה.
תוצרים
אנו מספקים את הפריטים הבאים כחלק מהגדרת הפיד:
- ביקורת על הפיד הנוכחי: בדיקת תקינות, חיפוש שגיאות סוג שדה, אופטימיזציה של גודל.
- פיתוח גנרטור עבור המחסנית שלך (Laravel, Symfony, WordPress, DRF).
- אינטגרציה עם מערכת ניהול המוצרים (ERP, CRM).
- בדיקות על הקטלוג המלא והשקה בארגז החול של Yandex.
- תיעוד לתחזוקה והדרכה למפתחים שלך.
- ניטור שגיאות והתראות בעת חסימה.
- גישה ללוח בקרה ליצירת פיד וחודש אחד של תמיכה לאחר ההשקה.
העלות הטיפוסית של פרויקט נעה בין $1,000 ל-$5,000, תלוי בגודל הקטלוג. לקוחות בדרך כלל חוסכים $2,000 בשנה רק על הפחתת שגיאות בפיד. במשך 5 שנים, הקמנו יותר מ-50 פידים עבור Yandex.Market, וכל אחד עבר את האימות בניסיון הראשון. צור קשר לבדיקת פיד.
שגיאות פיד נפוצות
Yandex.Market מחזיר דוח מפורט עבור כל הצעה. הבעיות הנפוצות ביותר:
- מחיר אפס או חסר — הפריט מוחרג אוטומטית מהאינדקס.
- אי-התאמת מחיר באתר — Yandex בודק את המחיר בפיד מול המחיר בעמוד המוצר. הפרש של יותר מ-1% חוסם את ההצעה.
- תמונה לא זמינה — נבדקת בטעינה הראשונית ושוב בכל סריקה.
- תיאור ארוך מדי — 3000 תווים מותרים עבור רוב הקטגוריות.
- ברקוד חסר — 50% מהשגיאות בכימיקלים ביתיים נובעות מברקודים חסרים.
בפרויקט אחד, גילינו ש-80% מהשגיאות נבעו מ-XMLWriter חסר עבור קטגוריית "כימיקלים ביתיים". הוספנו מילוי ברקוד ב-ERP — השגיאות נעלמו.
בעל חנות מקוונת אחת דיווח שאחרי הגדרת הפיד, מספר המוצרים המוחרגים ירד מ-2000 ל-0 בשבוע הראשון.
לוחות זמנים
גנרטור בסיסי לקטלוג סטנדרטי — 2–4 ימי עסקים. קטגוריות מורכבות (ביגוד עם רשת מידות, אלקטרוניקה עם מאפיינים מורחבים) — 4–6 ימי עסקים. הזמן ייעוץ — נעריך את הפרויקט שלך ונספק תוכנית מדויקת.







