נניח שבקטלוג שלך יש 50,000 מוצרים, ול-80% מהם אין תיאורים ומאפיינים. מילוי ידני ייקח למנהל תוכן שישה חודשים והשקעה משמעותית. ייבוא ממאגדי מחירים פותר זאת תוך ימים, ומספק חיסכון שנתי משמעותי. אנו מפתחים פרסרים ומערכות ייבוא במפתח מלא: מקובץ YML יחיד ועד מערכת רב-מאגדים עם עדיפויות. במהלך עבודתנו, אוטמטנו ייבוא ל-50+ חנויות מקוונות, תוך עיבוד של למעלה מ-2 מיליון מוצרים.
הבעיה הנפוצה ביותר היא חוסר התאמה בין פורמטים. Yandex.Market מספק YML, Price.ru — XML, OZON — JSON. ללא נורמליזציה, הנתונים הופכים לבלאגן. כאן נחלץ לעזרה תבנית המאגד (Adapter Pattern). היא מבודדת את הלוגיקה של כל מקור, ומאפשרת לשנות פורמט או להוסיף חדש מבלי לשנות קוד קיים.
אנו משתמשים בממשק אחיד לכל המקורות, מה שמאפשר לנו להוסיף מאגד חדש תוך יומיים. כתוצאה מכך, אתה מקבל קטלוג אחיד עם מחירים, מאפיינים ותמונות נכונים. ההמרה גדלה ב-15–20% בזכות שלמות הנתונים. זמן עדכון הקטלוג מצטמצם פי 5.
מקורות נתונים ממאגדים
| מאגד | פורמט נתונים | שיטת שליפה |
|---|---|---|
| Yandex.Market | YML (ייצוא מחירים) | ייצוא מחשבון אישי |
| Price.ru | XML / CSV | FTP או HTTP |
| E-Katalog | XML עם מאפיינים | API (בתשלום) או ייצוא |
| OZON | JSON דרך Seller API | REST API |
| Wildberries | JSON דרך Supplier API | REST API |
| Pricelist.ru | CSV | HTTP |
כל גישה שונה, אבל המטרה זהה: לנרמל נתונים ולהתאים אותם לסכמת קטלוג אחת.
איך לנרמל נתונים מפורמטים שונים?
שכבת מאגדים
interface AggregatorAdapterInterface {
public function fetchProducts(array $options = []): iterable;
public function getSupportedFields(): array;
public function getSourceId(): string;
}רישום במכלול השירותים:
$this->app->tag([
YandexMarketAdapter::class,
EKatalogAdapter::class,
OzonSellerAdapter::class,
WildberriesAdapter::class,
], 'aggregator.adapters');המאגדים מסתירים הבדלים ב-API ובפורמטים. מאגד חדש מתווסף באמצעות מימוש ממשק יחיד.
E-Katalog: מאפיינים והשוואות
E-Katalog הוא המקור העשיר ביותר למפרטים טכניים. בממוצע, הוא מספק 40% יותר מפרטים למוצר מאשר ייצוא ה-YML של Yandex.Market. ה-XML שלהם מכיל מאפיינים מתוקננים עם יחידות מידה.
לפי תיעוד ה-API של E-Katalog, ה-XML שלהם מכיל מאפיינים מתוקננים עם יחידות מידה.
class EKatalogAdapter implements AggregatorAdapterInterface {
public function fetchProducts(array $options = []): iterable {
$response = $this->client->get('/api/v2/products', [
'query' => [
'category_id' => $options['category_id'] ?? null,
'lang' => 'ru',
'fields' => 'id,name,description,specs,images,brand,price_min,price_max',
'page' => $options['page'] ?? 1,
'per_page' => 200,
],
'headers' => ['Authorization' => 'Bearer ' . $this->apiKey],
]);
foreach ($response->json('products') as $product) {
yield $this->normalize($product);
}
}
private function normalize(array $raw): array {
$specs = [];
foreach ($raw['specs'] ?? [] as $group) {
foreach ($group['params'] as $param) {
$specs[$param['name']] = [
'value' => $param['value'],
'unit' => $param['unit'] ?? null,
];
}
}
return [
'external_id' => 'ekatalog_' . $raw['id'],
'name' => $raw['name'],
'description' => $raw['description'],
'brand' => $raw['brand']['name'] ?? null,
'images' => array_column($raw['images'], 'url'),
'specs' => $specs,
'price_market_min' => $raw['price_min'],
'price_market_max' => $raw['price_max'],
];
}
}OZON Seller API מחזיר נתונים ב-JSON, אבל עם מגבלה של 100 מוצרים לבקשה. אנו משתמשים בדפדוף ובטעינה אצווה.
למה תבנית המאגד היא הבחירה הטובה ביותר?
השווה לפרסר מונוליטי: כל שינוי בפורמט שובר את כל המערכת. מאגדים מבודדים שינויים — מאגד חדש מתווסף תוך 1–2 ימים מבלי לגעת בקיימים. אנו משתמשים בגישה זו ב-50+ פרויקטים לאוטומציה של ייבוא. תבנית המאגד מעבדת 10,000 מוצרים פי 3 מהר יותר מאשר סקריפט מונוליטי ומפחיתה את זמן האינטגרציה של מקור פי 5.
| פרמטר | פרסר מונוליטי | תבנית מאגד |
|---|---|---|
| זמן הוספת מקור | 2–3 שבועות | יומיים |
| סיכון לשבירה בעת שינוי | גבוה | אפס |
| תחזוקת קוד | מורכבת | פשוטה |
רוצה ליישם גישה זו? קבל ייעוץ.
שימוש במחירי שוק לאנליטיקה
לאחסון נתונים, נוצרת טבלת interface AggregatorAdapterInterface { public function fetchProducts(array $options = []): iterable; public function getSupportedFields(): array; public function getSourceId(): string; } עם שדות: $this->app->tag([ YandexMarketAdapter::class, EKatalogAdapter::class, OzonSellerAdapter::class, WildberriesAdapter::class, ], 'aggregator.adapters'); , class EKatalogAdapter implements AggregatorAdapterInterface { public function fetchProducts(array $options = []): iterable { $response = $this->client->get('/api/v2/products', [ 'query' => [ 'category_id' => $options['category_id'] ?? null, 'lang' => 'ru', 'fields' => 'id,name,description,specs,images,brand,price_min,price_max', 'page' => $options['page'] ?? 1, 'per_page' => 200, ], 'headers' => ['Authorization' => 'Bearer ' . $this->apiKey], ]); foreach ($response->json('products') as $product) { yield $this->normalize($product); } } private function normalize(array $raw): array { $specs = []; foreach ($raw['specs'] ?? [] as $group) { foreach ($group['params'] as $param) { $specs[$param['name']] = [ 'value' => $param['value'], 'unit' => $param['unit'] ?? null, ]; } } return [ 'external_id' => 'ekatalog_' . $raw['id'], 'name' => $raw['name'], 'description' => $raw['description'], 'brand' => $raw['brand']['name'] ?? null, 'images' => array_column($raw['images'], 'url'), 'specs' => $specs, 'price_market_min' => $raw['price_min'], 'price_market_max' => $raw['price_max'], ]; } } , market_price_data, product_id, source, price_min, price_max. בהתבסס על נתונים אלה, ניתן להגדיר אוטומטית מחיר כ"מינימום שוק - 5%" או "2% מעל הממוצע" — תמחור דינמי המבוסס על נתונים אמיתיים. פתרון זה מגדיל את ההמרה בעד 15% בפרויקטים שלנו.
איך להוסיף מאגד חדש: מדריך שלב אחר שלב
- צור מחלקת מאגד המיישמת
price_avg. - רשום את המאגד במכלול השירותים עם התג
offers_count. - יישם מיפוי שדות מהמקור לסכמה האחידה.
- בדוק על מדגם של 200 מוצרים.
- הפעל טעינה מלאה.
העשרת מוצרים קיימים
מקרה השימוש העיקרי: בקטלוג יש מוצר עם SKU אבל ללא מאפיינים ותיאור. המאגד מכיר מוצר זה לפי GTIN או שם מותג+דגם. אנו מעשירים רק שדות ריקים מבלי לדרוס עריכות ידניות.
class ProductEnrichmentService {
public function enrich(Product $product): bool {
// Поиск по GTIN у агрегаторов
foreach ($this->adapters as $adapter) {
$data = $adapter->findByGtin($product->gtin);
if (!$data) $data = $adapter->findByBrandModel($product->brand, $product->model);
if (!$data) continue;
$this->applyEnrichment($product, $data, $adapter->getSourceId());
return true;
}
return false;
}
private function applyEnrichment(Product $product, array $data, string $source): void {
// Обогащаем только пустые поля — не перезаписываем существующее
if (!$product->description && !empty($data['description'])) {
$product->description = $data['description'];
$product->description_source = $source;
}
if (empty($product->specs) && !empty($data['specs'])) {
foreach ($data['specs'] as $name => $spec) {
ProductSpec::updateOrCreate(
['product_id' => $product->id, 'name' => $name],
['value' => $spec['value'], 'unit' => $spec['unit'], 'source' => $source]
);
}
}
$product->save();
}
} איך פועל הסרת כפילויות?
דוגמה להגדרת עדיפות מקור: מערך collected_at מגדיר איזה מקור נחשב ראשי. מספר גבוה יותר פירושו עדיפות גבוהה יותר. במקרה של קונפליקט, המקור בעל העדיפות הגבוהה יותר מנצח.
private array $sourcePriority = [
'manufacturer_direct' => 100,
'ekatalog' => 80,
'yandex_market' => 70,
'ozon' => 60,
'price_ru' => 50,
];
הסרת כפילויות מתבצעת לפי מזהה חיצוני (GTIN, SKU). אם שני מקורות מספקים את אותו מוצר, הנתונים נלקחים מהמקור בעל העדיפות הגבוהה יותר. זה מבטל כפילויות וקונפליקטים.
לוח זמנים ליישום
- מאגד אחד (YML מ-Yandex.Market), העשרת שדות ריקים — יומיים
- מבנה רב-מאגדים + עדיפויות + מחירי שוק — +יומיים
- OZON/WB API, תמחור דינמי לפי שוק — +2–3 ימים
מה כלול בעבודה
- פיתוח והגדרת מאגדים לכל מקור
- נורמליזציה של נתונים והסרת כפילויות
- העשרת שדות ריקים (תיאורים, מאפיינים, תמונות)
- הכנת תיעוד על מבנה הנתונים ותהליך העדכון
- העברת גישה למערכת (חשבון אישי, FTP, מפתחות API)
- הדרכת מנהלי תוכן לעבודה עם ייבוא
- תמיכה טכנית לחודש לאחר ההשקה
לוח הזמנים הסופי תלוי במספר המאגדים ובמורכבות הנורמליזציה. צור קשר להערכה חינמית של הפרויקט שלך. הזמן ייבוא מוצרים וקבל קטלוג עם נתונים מלאים תוך יומיים.







