כשניטור ידני מנקז את התקציב שלך
החנות שלך ב-Wildberries מפסידה עד 30% מההכנסות בגלל מחירי מתחרים מיושנים? בדיקה ידנית של 15,000 פריטים היא 5 ימי עבודה בשבוע, וטעות בתמחור יכולה לעלות בערך $9–13 בחיסכון. אנחנו, צוות עם 5 שנות ניסיון ו-40+ פרויקטי גירוד מוצלחים, יודעים כיצד להפוך תהליך זה לאוטומטי ללא סיכון של חסימה. אנו מציעים פתרון סוהר מניתוח ועד אינטגרציה עם ה-CRM שלך.
במאמר זה נסקור שלוש אסטרטגיות לאיסוף נתונים עבור מרקטפלייסים: שימוש ב-API רשמיים, גירוד נקודות קצה ציבוריות של JSON, ואוטומציה של דפדפן עם עקיפת Cloudflare. תקבלו קטעי קוד מוכנים עבור Wildberries, Ozon ו-Amazon, וכן רשימת בדיקה למניעת זיהוי כדי להבטיח פעולה יציבה.
API רשמיים לעומת גירוד: מה לבחור
לפני כתיבת סקריפט גירוד, בדקו את היכולות הרשמיות. API מספקים נתונים מובנים אך מוגבלים למוצרים שלכם. לצורך ניתוח תחרותי, תצטרכו לגרד.
| מרקטפלייס | API רשמי | מגבלות |
|---|---|---|
| Ozon | Seller API (למוכרים) | רק המוצרים שלכם |
| Wildberries | Seller API, Statistics API | רק הנתונים שלכם |
| Amazon | Product Advertising API | דורש שותפות |
| Yandex.Market | Partner API | לשותפים |
גירוד מוצרים של מוכרים אחרים הוא אזור אפור בתנאי השירות. אנו משתמשים בזה אך ורק לניתוח תחרותי, ניטור מחירים ומחקר שוק. API חוקיים הם קו הבסיס; גירוד מרחיב אותם.
איך לעקוף את הגנת Cloudflare ב-Ozon?
Ozon בונה דפים עם React; נתונים מועברים דרך בקשות XHR. Cloudflare בודק את סביבת ה-JavaScript, כך ש-requests פשוט לא יעבוד. הפתרון שלנו: Playwright עם אמולציית דפדפן אמיתית, יירוט תגובות API וסיבוב User-Agent. הנה דוגמה לסקריפט גירוד:
# scraper/ozon.py
from playwright.async_api import async_playwright
import json
class OzonScraper:
async def scrape_product(self, url: str) -> dict:
async with async_playwright() as p:
browser = await p.chromium.launch(headless=True)
context = await browser.new_context(
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
viewport={"width": 1366, "height": 768},
)
# Перехватываем API-ответы с данными товара
product_data = {}
async def handle_response(response):
if "/api/entrypoint-api.bx/page/json" in response.url:
try:
data = await response.json()
widget_states = data.get("widgetStates", {})
for key, value in widget_states.items():
if "webProductHeading" in key:
product_data["heading"] = json.loads(value)
elif "webPrice" in key:
product_data["price"] = json.loads(value)
except Exception:
pass
context.on("response", handle_response)
page = await context.new_page()
await page.goto(url, wait_until="networkidle")
await browser.close()
return self._normalize_ozon(product_data)
def _normalize_ozon(self, data: dict) -> dict:
heading = data.get("heading", {})
price = data.get("price", {})
return {
"name": heading.get("title"),
"sku": heading.get("sku"),
"price": self._parse_price(price.get("price", "")),
"original_price": self._parse_price(price.get("originalPrice", "")),
"discount": price.get("discount"),
}
def _parse_price(self, s: str) -> float:
return float("".join(c for c in s if c.isdigit() or c == ".") or 0)Playwright יציב פי 3 מ-Selenium באתרים דינמיים בזכות המתנה מובנית ותמיכה בדפדפנים מודרניים. להגנה נוספת, אנו משתמשים ב-# scraper/ozon.py from playwright.async_api import async_playwright import json class OzonScraper: async def scrape_product(self, url: str) -> dict: async with async_playwright() as p: browser = await p.chromium.launch(headless=True) context = await browser.new_context( user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64)", viewport={"width": 1366, "height": 768}, ) # Перехватываем API-ответы с данными товара product_data = {} async def handle_response(response): if "/api/entrypoint-api.bx/page/json" in response.url: try: data = await response.json() widget_states = data.get("widgetStates", {}) for key, value in widget_states.items(): if "webProductHeading" in key: product_data["heading"] = json.loads(value) elif "webPrice" in key: product_data["price"] = json.loads(value) except Exception: pass context.on("response", handle_response) page = await context.new_page() await page.goto(url, wait_until="networkidle") await browser.close() return self._normalize_ozon(product_data) def _normalize_ozon(self, data: dict) -> dict: heading = data.get("heading", {}) price = data.get("price", {}) return { "name": heading.get("title"), "sku": heading.get("sku"), "price": self._parse_price(price.get("price", "")), "original_price": self._parse_price(price.get("originalPrice", "")), "discount": price.get("discount"), } def _parse_price(self, s: str) -> float: return float("".join(c for c in s if c.isdigit() or c == ".") or 0) — תוסף שמסתיר אוטומציה.
למה קל יותר לגרד את Wildberries?
ל-Wildberries יש API ציבוריים של JSON שאינם דורשים אימות. הם עובדים ישירות, ללא JavaScript, מה שמפשט את איסוף הנתונים. דוגמה לסקריפט באמצעות playwright-stealth ו-httpx:
# scraper/wildberries.py
import httpx
import asyncio
from typing import Optional
class WildberriesScraper:
CARD_URL = "https://card.wb.ru/cards/v2/detail"
SEARCH_URL = "https://search.wb.ru/exactmatch/ru/common/v9/search"
CATALOG_URL = "https://catalog.wb.ru/catalog/{shard}/v2/catalog"
def __init__(self):
self.client = httpx.AsyncClient(
headers={
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
"Accept": "*/*",
"Origin": "https://www.wildberries.ru",
"Referer": "https://www.wildberries.ru/",
},
timeout=15,
)
async def get_product(self, nm_id: int) -> Optional[dict]:
"""Получение карточки товара по артикулу WB"""
params = {
"appType": 1,
"curr": "rub",
"dest": -1257786, # Москва
"nm": nm_id,
}
resp = await self.client.get(self.CARD_URL, params=params)
resp.raise_for_status()
data = resp.json()
products = data.get("data", {}).get("products", [])
if not products:
return None
return self._normalize_product(products[0])
def _normalize_product(self, raw: dict) -> dict:
sizes = raw.get("sizes", [])
price_data = sizes[0].get("price", {}) if sizes else {}
return {
"nm_id": raw["id"],
"name": raw.get("name"),
"brand": raw.get("brand"),
"supplier_id": raw.get("supplierId"),
"rating": raw.get("reviewRating"),
"feedbacks": raw.get("feedbacks"),
"price": price_data.get("product", 0) / 100,
"sale_price": price_data.get("total", 0) / 100,
"discount": raw.get("sale", 0),
"colors": [c["name"] for c in raw.get("colors", [])],
}
async def search_products(self, query: str, page: int = 1) -> list[dict]:
params = {
"appType": 1,
"curr": "rub",
"dest": -1257786,
"page": page,
"query": query,
"resultset": "catalog",
"sort": "popular",
}
resp = await self.client.get(self.SEARCH_URL, params=params)
resp.raise_for_status()
products = resp.json().get("data", {}).get("products", [])
return [self._normalize_product(p) for p in products]
async def scrape_category(self, shard: str, query: str, pages: int = 5) -> list[dict]:
"""Обход категории постранично"""
all_products = []
for page in range(1, pages + 1):
products = await self.search_products(query, page)
if not products:
break
all_products.extend(products)
await asyncio.sleep(1.5) # Пауза между запросами
return all_products
שימו לב ל-asyncio — הפסקה חובה בין בקשות כדי למנוע הגבלת קצב. לאיסוף בקנה מידה גדול, אנו מוסיפים סיבוב פרוקסי דרך Bright Data או IPRoyal.
Amazon: API רשמי אמין יותר
עבור Amazon, אנו ממליצים על Product Advertising API 5.0. הוא מספק גישה למחירים, דירוגים ותיאורים. גירוד דפדפן כאן פחות יעיל בגלל הגנה אגרסיבית. דוגמה:
# scraper/amazon_pa.py
from paapi5_python_sdk import DefaultApi, SearchItemsRequest, PartnerType
class AmazonScraper:
def __init__(self, access_key: str, secret_key: str, partner_tag: str):
self.api = DefaultApi(
access_key=access_key,
secret_key=secret_key,
host="webservices.amazon.com",
region="us-east-1",
)
self.partner_tag = partner_tag
def search_products(self, keywords: str, category: str = "All") -> list[dict]:
request = SearchItemsRequest(
partner_tag=self.partner_tag,
partner_type=PartnerType.ASSOCIATES,
keywords=keywords,
search_index=category,
item_count=10,
resources=[
"ItemInfo.Title",
"Offers.Listings.Price",
"Images.Primary.Large",
"ItemInfo.Features",
],
)
response = self.api.search_items(request)
return [self._normalize(item) for item in response.search_result.items]
def _normalize(self, item) -> dict:
price = None
if item.offers and item.offers.listings:
price = item.offers.listings[0].price.amount
return {
"asin": item.asin,
"title": item.item_info.title.display_value if item.item_info else None,
"price": price,
"image": item.images.primary.large.url if item.images else None,
"url": item.detail_page_url,
}ה-API דורש חשבון שותף, אבל הנתונים חוקיים ומובנים. לנפחים קטנים, זו האפשרות הטובה ביותר.
תזמור סקריפטים ב-Laravel
יש לאחסן ולעדכן את הנתונים שנאספו. בפרויקטים שלנו, אנו משתמשים ב-Laravel עם תורים וסקריפטים של Python המופעלים דרך Process:
// app/Console/Commands/ScrapeMarketplace.php
class ScrapeMarketplace extends Command
{
protected $signature = 'scrape:marketplace {marketplace} {--query=} {--pages=5}';
public function handle(): void
{
$marketplace = $this->argument('marketplace');
$query = $this->option('query');
$pages = (int) $this->option('pages');
$process = new Process([
'python3',
base_path('scraper/run.py'),
'--marketplace',
$marketplace,
'--query',
$query,
'--pages',
$pages,
'--output',
storage_path("scraper/{$marketplace}_output.json"),
]);
$process->setTimeout(300)->run();
if ($process->isSuccessful()) {
$data = json_decode(file_get_contents(
storage_path("scraper/{$marketplace}_output.json")
), true);
foreach ($data as $item) {
MarketplaceProduct::updateOrCreate(
['marketplace' => $marketplace, 'external_id' => $item['nm_id'] ?? $item['asin']],
$item + ['scraped_at' => now()]
);
}
$this->info("Импортировано: " . count($data) . " товаров");
} else {
Log::error($process->getErrorOutput());
}
}
}ארכיטקטורה זו מקלה על קנה מידה: הוסיפו מרקטפלייס חדש, כתבו סקריפט נפרד, והריצו את אותה פקודה.
אמצעי מניעת זיהוי: רשימת בדיקה
| איום | פתרון |
|---|---|
| חסימת IP | פרוקסי מתחלף (Bright Data, IPRoyal) |
| טביעת אצבע של User-Agent | אקראיות + עדכון |
| טביעת אצבע של דפדפן | תוסף stealth של Playwright |
| הגבלת קצב | הפסקות אקראיות 1-5 שניות |
| CAPTCHA | 2captcha / anti-captcha API |
| קישורי Honeypot | סינון קישורים בלתי נראים |
אנו מבטיחים שהתצורה שלנו עוברת 99% מבדיקות Cloudflare.
מה כלול בפיתוח סקריפט גירוד סוהר
- ניתוח המרקטפלייס היעד ובחירת האסטרטגיה האופטימלית (API, גירוד, דפדפן)
- כתיבת הסקריפט ב-Python עם asyncio או Playwright
- הגדרת סיבוב פרוקסי ו-User-Agent
- נורמליזציה של נתונים: תקנון מחירים, הסרת כפילויות, ניקוי HTML
- אינטגרציה עם ה-CRM, ERP או Google Sheets שלכם דרך REST API או CSV
- תיעוד והדרכת צוות
- תמיכה ל-3 חודשים — תיקון תקלות כשהאתר משתנה
לוח זמנים לפיתוח
| מרקטפלייס | מורכבות | לוח זמנים |
|---|---|---|
| Wildberries (JSON API) | בינוני | 3-5 ימים |
| Ozon (Playwright) | גבוה | 5-8 ימים |
| Amazon (PA API) | נמוך | 2-3 ימים |
| Yandex.Market | בינוני | 3-5 ימים |
| + ניטור והתראות | +2-3 ימים |
נעריך את הפרויקט שלכם. צרו קשר לייעוץ — נבחר את הארכיטקטורה ולוח הזמנים האופטימליים. הזמינו פיתוח סקריפט גירוד סוהר כבר עכשיו.
תיעוד Playwright







