נקודת הקצה /products.json של Shopify היא אחד הסודות הגלויים ביותר בעולם נתוני האיקומרס. פשוט מוסיפים אותה לכתובת של כל חנות Shopify, ומקבלים בחזרה JSON מובנה — בלי מפתחות API, בלי אימות, ובלי צורך לגרד HTML מקונן.
אני עובד בצוות של , ולכן אני מבלה לא מעט זמן במחשבה על האופן שבו אנשים מחלצים נתונים מהאינטרנט. וגרידה מ‑Shopify עולה שוב ושוב — צוותי מכירות שעוקבים אחרי מחירי מתחרים, אנשי תפעול באיקומרס שמשווים קטלוגים, ואנשי רכש שמאתרים ספקים חדשים. עם ב‑Shopify ונתח של כ‑, כמות נתוני המוצרים שאפשר לגרד היא עצומה.
המדריך הזה מכסה את כל התהליך: מה מחזירה נקודת הקצה, איך עוברים בין אלפי מוצרים בעזרת pagination, איך מתמודדים עם מגבלות קצב בלי להיחסם, ואיך משטחים את ה‑JSON המקונן של Shopify לקובץ CSV או Excel מסודר באמצעות pandas. אכסה גם נקודות קצה שכמעט אף אחד לא מדבר עליהן (/collections.json, /meta.json) ואציג חלופה ללא קוד למי שמעדיף לוותר לגמרי על Python.
מהי נקודת הקצה /products.json של Shopify, ולמה היא הופכת את הגרידה לקלה כל כך?
לכל חנות Shopify יש נקודת קצה ציבורית בכתובת {store-url}/products.json שמחזירה נתוני מוצרים מובנים. בלי מפתחות API. בלי OAuth. בלי שום אימות. פשוט מוסיפים /products.json לכתובת החנות ומקבלים מערך JSON של כל המוצרים בקטלוג.
נסו בעצמכם עכשיו: פתחו בדפדפן את או את . תראו JSON נקי ומובנה עם כותרות מוצרים, מחירים, וריאנטים, תמונות, תגיות — הכול.
השוו זאת לחלופה: ניתוח של תבניות ה‑HTML של Shopify, שהן מקוננות מאוד, לא אחידות בין חנויות, ומשתנות בכל פעם שסוחר מעדכן את התבנית שלו. הנה מה שבדרך כלל תצטרכו להתמודד איתו:
שיטת ה‑HTML (כואבת):
1<div class="product-card__info">
2 <h3 class="product-card__title">
3 <a href="/products/classic-blue-jeans">Classic Blue Jeans</a>
4 </h3>
5 <span class="price price--on-sale" data-product-price>$149.00</span>
6</div>
שיטת ה‑JSON (נקייה):
1{
2 "title": "Classic Blue Jeans",
3 "handle": "classic-blue-jeans",
4 "vendor": "Hiut Denim",
5 "variants": [{"price": "149.00", "sku": "HD-BLU-32", "available": true}]
6}
JSON מנצח בעקביות, אמינות וקלות ניתוח. נקודת הקצה תומכת גם בשני פרמטרי שאילתה חשובים — ?limit= (עד 250 מוצרים לעמוד, ברירת המחדל היא 30) ו‑?page= ל‑pagination — ונשתמש בהם הרבה בקוד שבהמשך.
הבחנה חשובה: זו נקודת קצה ציבורית של חנות הוויטרינה, לא ה‑. ה‑Admin API דורש אסימוני גישה של בעל החנות ומספק נתוני הזמנות, רמות מלאי ומידע על לקוחות. לעומת זאת, /products.json הציבורית מחזירה רק נתוני מוצר לקריאה בלבד, שכל אחד יכול לגשת אליהם. אסביר את ההבדל בהמשך, כי יש הרבה בלבול סביב זה בפורומים.
הערת זהירות: לא כל חנות Shopify חושפת את הנקודה הזו. בבדיקות שלי, בערך 71% מהחנויות מחזירות JSON תקין (allbirds.com, gymshark.com, colourpop.com, kyliecosmetics.com — כולן עובדות), בעוד שחלק מהתצורות המותאמות אישית מחזירות 404 (hiutdenim.co.uk, bombas.com). בדיקה מהירה פשוטה: נכנסים ל‑{store-url}/products.json בדפדפן ורואים מה מתקבל.
למה לגרד Shopify עם Python? מקרי שימוש עסקיים מובילים
למה בכלל להתאמץ? ROI. כבר משתמשים בגרידת מחירים אוטומטית לצורך מודיעין תחרותי, לעומת 34% בלבד בשנת 2020. ומחקרים מראים ששיפור של . הנתונים האלה שווים כסף אמיתי.
אלו מקרי השימוש הנפוצים ביותר שאני רואה:
| מקרה שימוש | מי מרוויח | מה מקבלים |
|---|---|---|
| מעקב אחר מחירי מתחרים | צוותי תפעול באיקומרס | מעקב אחר שינויי מחירים, הנחות ומחירי השוואה בקטלוגים של מתחרים |
| מחקר מוצרים ואיתור מקורות אספקה | רכש / מרצ'נדייזינג | השוואת מאפייני מוצר, וריאנטים, חומרים וזמינות |
| יצירת לידים | צוותי מכירות | חילוץ שמות ספקים, נתוני מותג ופרטי קשר מקטלוגים |
| ניתוח שוק וקטגוריות | צוותי שיווק | הבנה של תמהיל מוצרים, תגיות, מבנה קולקציות ומיצוב |
| מעקב אחר מלאי וזמינות | צוותי שרשרת אספקה | ניטור מצב מלאי ברמת וריאנט (available: true/false) לאורך זמן |
| זיהוי מוצרים חדשים | צוותי מוצר | מעקב אחר חותמות זמן של created_at כדי לזהות השקות חדשות של מתחרים |
Python הוא הכלי הטבעי למשימה הזו. משתמשים ב‑Python כשפת העבודה הראשית שלהם, והאקוסיסטם — requests ל‑HTTP, pandas לעיבוד נתונים, httpx לעבודה אסינכרונית — הופך את הדרך מ‑"יש לי URL" ל‑"יש לי גיליון נתונים" לפשוטה מאוד, אפילו בפחות מ‑80 שורות קוד.
פירוט מלא של שדות products.json: הסבר על כל שדה
בכל מדריך מתחרה יראו לכם את title, id ו‑handle, ואז יעברו הלאה. אבל תגובת ה‑JSON של Shopify מכילה יותר מ‑40 שדות על פני מוצרים, וריאנטים, תמונות ואפשרויות. לדעת מה זמין לפני שכותבים את קוד הגרידה חוסך גרידות חוזרות בהמשך.
הפירוט הזה מבוסס על תגובות חיות של /products.json שנשלפו ב‑16 באפריל 2026. המבנה עקבי בכל החנויות שחושפות את הנקודה הזו.
שדות ברמת המוצר
| שדה | סוג נתונים | ערך לדוגמה | שימוש עסקי |
|---|---|---|---|
id | מספר שלם | 123456789 | מזהה מוצר ייחודי לצורך מניעת כפילויות |
title | מחרוזת | "Classic Blue Jeans" | שם המוצר לקטלוגים והשוואות |
handle | מחרוזת | "classic-blue-jeans" | סלאג של ה‑URL — בונים קישור לעמוד המוצר כ‑{store}/products/{handle} |
body_html | מחרוזת (HTML) או null | Our best-selling... | תיאור מוצר לניתוח תוכן ומחקר SEO |
vendor | מחרוזת | "Hiut Denim" | שם מותג/ספק ליצירת לידים או איתור מקורות |
product_type | מחרוזת | "Jeans" | סיווג קטגוריה לניתוח שוק |
created_at | תאריך ושעה ISO | "2024-01-15T10:30:00-05:00" | מעקב מתי המוצרים נוספו (זיהוי השקות חדשות) |
updated_at | תאריך ושעה ISO | "2025-03-01T08:00:00-05:00" | זיהוי שינויים עדכניים בקטלוג |
published_at | תאריך ושעה ISO | "2024-01-16T00:00:00-05:00" | לדעת מתי המוצרים עלו לחנות |
tags | מערך מחרוזות | ["organic", "women", "straight-leg"] | ניתוח תגיות/מילות מפתח ל‑SEO, סיווג וזיהוי מגמות |
variants | מערך אובייקטים | (ראו שדות וריאנט בהמשך) | מחיר, SKU וזמינות לכל וריאנט |
images | מערך אובייקטים | (ראו שדות תמונה בהמשך) | כתובות תמונה לקטלוגים ולניתוח חזותי |
options | מערך אובייקטים | [{"name": "Size", "values": ["S","M","L"]}] | הבנת תצורת המוצר (מידה, צבע, חומר) |
שדות ברמת וריאנט (מקוננים תחת כל מוצר)
| שדה | סוג נתונים | דוגמה | שימוש |
|---|---|---|---|
id | מספר שלם | 987654321 | מזהה וריאנט ייחודי |
title | מחרוזת | "32 / Blue" | שם התצוגה של הווריאנט |
sku | מחרוזת | "HD-BLU-32" | התאמת SKU למערכות מלאי |
price | מחרוזת | "185.00" | מעקב מחירים (שימו לב: זו מחרוזת, צריך להמיר ל‑float לחישובים) |
compare_at_price | מחרוזת או null | "200.00" | המחיר המקורי — חיוני למעקב אחר הנחות |
available | בוליאני | true | זמינות במלאי (האינדיקטור הציבורי היחיד למלאי) |
weight | מספר עשרוני | 1.2 | ניתוח שילוח/לוגיסטיקה |
option1, option2, option3 | מחרוזת | "32", "Blue", null | ערכי האפשרויות הבודדות |
created_at, updated_at | תאריך ושעה ISO | — | מעקב שינויים ברמת וריאנט |
שדות ברמת תמונה
| שדה | סוג נתונים | דוגמה | שימוש |
|---|---|---|---|
id | מספר שלם | 111222333 | מזהה תמונה ייחודי |
src | מחרוזת (URL) | "https://cdn.shopify.com/..." | קישור ישיר להורדת התמונה |
alt | מחרוזת או null | "Front view of jeans" | טקסט חלופי של התמונה לצורכי נגישות |
position | מספר שלם | 1 | סדר הופעת התמונות |
width, height | מספר שלם | 2048, 2048 | ממדי התמונה |
מה לא נמצא בנקודת הקצה הציבורית
נקודת כשל חשובה: inventory_quantity לא זמין בתגובות הציבוריות של /products.json. השדה הזה הוסר מנקודות קצה ציבוריות בפורמט JSON בדצמבר 2017 מסיבות אבטחה. האינדיקטור היחיד למלאי שתקבלו הוא השדה הבוליאני available בכל וריאנט (true או false). כדי לגשת לכמות מלאי אמיתית צריך להשתמש ב‑Admin API המאומת, עם אישורי בעל החנות.
לפני שכותבים את קוד הגרידה, עברו על הטבלה הזו והחליטו אילו שדות חשובים לכם. אם אתם עושים מעקב מחירים, תצטרכו את variants[].price, את variants[].compare_at_price ואת variants[].available. אם אתם עושים יצירת לידים, התמקדו ב‑vendor, ב‑product_type וב‑tags. סננו בהתאם — ה‑CSV שלכם יהיה הרבה יותר נקי.
מעבר ל‑products.json: קולקציות, מטא ונקודות קצה נוספות של Shopify
אף מדריך מתחרה לא מזכיר את הנקודות האלה. הן חיוניות לעבודת מודיעין תחרותי רצינית.
/collections.json — כל הקטגוריות בחנות
מחזיר את כל הקולקציות (הקטגוריות) בחנות, עם כותרות, handles, תיאורים וכמות מוצרים. בדקתי זאת ב‑zoologistperfumes.com, allbirds.com ו‑gymshark.com — כולן החזירו JSON תקין.
1{
2 "collections": [
3 {
4 "id": 308387348539,
5 "title": "Attars",
6 "handle": "attars",
7 "published_at": "2026-03-29T12:20:32-04:00",
8 "products_count": 1,
9 "image": { "src": "https://cdn.shopify.com/..." }
10 }
11 ]
12}
רוצים להבין איך מתחרה מארגן את הקטלוג שלו? זו הנקודה המתאימה.
/collections/{handle}/products.json — מוצרים לפי קטגוריה
מחזיר מוצרים מסוננים לפי קולקציה מסוימת. זהו אותו מבנה JSON כמו ב‑/products.json, אבל מוגבל לקטגוריה אחת. זה קריטי לגרידה ברמת קטגוריה — למשל אם רוצים לעקוב רק אחרי הקולקציה "Sale" או "New Arrivals" של מתחרה.
/meta.json — מטא־נתונים של החנות
מחזיר שם חנות, תיאור, מטבע, מדינה — והחלק המעניין — published_products_count. המספר הזה מאפשר לחשב מראש בדיוק כמה עמודי pagination תצטרכו: ceil(published_products_count / 250). כך כבר לא צריך להמשיך להעלות עמודים בצורה עיוורת עד שמתקבלת תשובה ריקה.
באיזו נקודת קצה כדאי להשתמש?
| מה רוצים לקבל | נקודת קצה | צריך אימות? |
|---|---|---|---|
| כל המוצרים (ציבורי) | /products.json | לא |
| מוצרים בקטגוריה מסוימת | /collections/{handle}/products.json | לא |
| מטא־נתונים של החנות + כמות מוצרים | /meta.json | לא |
| כל הקולקציות (קטגוריות) | /collections.json | לא |
| נתוני הזמנות/מכירות (רק לחנות שלך) | Admin API /orders.json | כן (API key) |
| כמויות מלאי (רק לחנות שלך) | Admin API /inventory_levels.json | כן |
השאלה שחוזרת שוב ושוב בפורומים — "אפשר לגרד כמה יחידות מתחרה מכר?" — מקבלת תשובה קצרה: לא. לא מנקודות קצה ציבוריות. נתוני מכירות וכמויות מלאי דורשים את ה‑Admin API המאומת, כלומר צריך גישת בעלים לחנות. נקודות קצה ציבוריות נותנות רק נתוני קטלוג מוצרים.

איך לגרד Shopify עם Python: הגדרה שלב אחר שלב
- רמת קושי: מתחילים
- זמן נדרש: כ‑15 דקות (התקנה + גרידה ראשונה)
- מה צריך: Python 3.11+,
pip, טרמינל, וכתובת URL של חנות Shopify לגרידה
שלב 1: התקנת Python והספריות הדרושות
ודאו שמותקנת אצלכם גרסת Python 3.11 ומעלה (pandas 3.0.x דורש זאת). לאחר מכן התקינו את שתי הספריות שנצטרך:
1pip install requests pandas
לייצוא ל‑Excel, תצטרכו גם:
1pip install openpyxl
בראש הסקריפט הוסיפו את הייבוא הבא:
1import requests
2import pandas as pd
3import time
4import random
5import json
לא אמורות להופיע שגיאות ייבוא כשתריצו את הסקריפט. אם pandas זורקת שגיאת גרסה, שדרגו ל‑Python 3.12.
שלב 2: שליפת נתוני מוצרים מ‑/products.json
הנה פונקציה בסיסית שלוקחת כתובת חנות, פונה לנקודת הקצה ומחזירה JSON מפוענח:
1def fetch_products_page(store_url, page=1, limit=250):
2 """Fetch a single page of products from a Shopify store."""
3 url = f"{store_url.rstrip('/')}/products.json"
4 params = {"limit": limit, "page": page}
5 headers = {
6 "User-Agent": "Mozilla/5.0 (compatible; ProductResearch/1.0)"
7 }
8 response = requests.get(url, params=params, headers=headers, timeout=30)
9 response.raise_for_status()
10 return response.json().get("products", [])
פרטים חשובים:
limit=250הוא המקסימום ש‑Shopify מאפשרת לעמוד. ברירת המחדל היא 30, ולכן הגדרה מפורשת של הערך הזה מפחיתה את מספר הבקשות עד פי 8.- כותרת
User-Agent: תמיד הגדירו אחת ריאליסטית. בקשות בלי User-Agent נוטות יותר להפעיל את מנגנוני האנטי‑בוט של Shopify. timeout=30: אל תיתנו לבקשה אחת להיתקע לנצח.
בדקו את זה מול חנות מוכרת:
1products = fetch_products_page("https://allbirds.com")
2print(f"Fetched {len(products)} products")
3print(f"First product: {products[0]['title']}")
אתם אמורים לראות משהו כמו: Fetched 250 products ואת כותרת המוצר הראשון.
שלב 3: טיפול ב‑pagination כדי לגרד את כל המוצרים
בקשה אחת מחזירה לכל היותר 250 מוצרים. לרוב החנויות יש יותר מזה (ל‑Allbirds יש 1,420+). לכן צריך לעבור בלולאה בין עמודים עד שמתקבלת תשובה ריקה.
1def scrape_all_products(store_url, delay=1.0):
2 """Scrape all products from a Shopify store, handling pagination."""
3 all_products = []
4 page = 1
5 while True:
6 print(f"Fetching page {page}...")
7 products = fetch_products_page(store_url, page=page, limit=250)
8 if not products:
9 print(f"No more products. Total: {len(all_products)}")
10 break
11 all_products.extend(products)
12 print(f" Got {len(products)} products (total so far: {len(all_products)})")
13 page += 1
14 # Be polite: wait between requests
15 time.sleep(delay + random.uniform(0, 0.5))
16 return all_products
כש‑products חוזר ריק, הגעתם לסוף.
ה‑time.sleep() עם jitter אקראי עוזר להישאר מתחת למגבלת הקצב הלא רשמית של Shopify — בערך 2 בקשות לשנייה.
טיפ מקצועי: אם שלפתם קודם את /meta.json, כבר יש לכם את מספר המוצרים הכולל, ואפשר לחשב בדיוק כמה עמודים צריך: pages = ceil(product_count / 250). כך נמנעים מהדפוס של "עוד בקשה ריקה אחת בסוף".
שלב 4: ניתוח ובחירת השדות שאתם צריכים
עכשיו, כשיש לכם את כל המוצרים כרשימת מילונים ב‑Python, אפשר לחלץ רק את השדות שרלוונטיים לכם. הנה דוגמה שמושכת את השדות הנפוצים ביותר למעקב מחירים:
1def extract_product_data(products):
2 """Extract key fields from products, flattening variants."""
3 rows = []
4 for product in products:
5 for variant in product.get("variants", []):
6 rows.append({
7 "product_id": product["id"],
8 "title": product["title"],
9 "handle": product["handle"],
10 "vendor": product.get("vendor", ""),
11 "product_type": product.get("product_type", ""),
12 "tags": ", ".join(product.get("tags", [])),
13 "created_at": product.get("created_at", ""),
14 "variant_id": variant["id"],
15 "variant_title": variant.get("title", ""),
16 "sku": variant.get("sku", ""),
17 "price": variant.get("price", ""),
18 "compare_at_price": variant.get("compare_at_price", ""),
19 "available": variant.get("available", ""),
20 "image_url": product["images"][0]["src"] if product.get("images") else ""
21 })
22 return rows
כך מתקבלת שורה אחת לכל וריאנט — הפורמט הכי שימושי להשוואת מחירים, כי מוצר אחד כמו "Classic Blue Jeans" יכול להכיל 12 וריאנטים (6 מידות × 2 צבעים), ולכל אחד מחיר ומצב זמינות משלו.
ייצוא נתוני Shopify שנגרדו ל‑CSV ו‑Excel עם pandas
כל מדריך אחר לגרידת Shopify זורק JSON גולמי לקובץ ומסיים שם. זה אולי מספיק למפתחים. פחות מועיל לאנליסט באיקומרס שצריך גיליון נתונים עד יום שישי.
הבעיה היא שה‑JSON של Shopify מקונן. מוצר אחד יכול לכלול תריסר וריאנטים, ולכל וריאנט מחיר, SKU וזמינות משלו. כדי להפוך את זה לשורות ועמודות צריך קצת עבודה עם pandas.
שטיחה של JSON מקונן לטבלה נקייה
יש שתי גישות, בהתאם לצורך שלכם:
אפשרות א: שורה אחת לכל וריאנט (הכי טוב למעקב מחירים ומלאי)
1# Using the extract_product_data function from Step 4
2products = scrape_all_products("https://allbirds.com")
3rows = extract_product_data(products)
4df = pd.DataFrame(rows)
5print(f"DataFrame shape: {df.shape}")
6print(df.head())
כך מתקבלת טבלה שטוחה שבה כל שורה היא שילוב ייחודי של מוצר+וריאנט. חנות עם 500 מוצרים ובממוצע 4 וריאנטים למוצר תיצור DataFrame של כ‑2,000 שורות.
אפשרות ב: שורה אחת לכל מוצר כסיכום (הכי טוב לסקירה כללית של הקטלוג)
1def summarize_products(products):
2 """One row per product with min/max price across variants."""
3 rows = []
4 for product in products:
5 prices = [float(v["price"]) for v in product.get("variants", []) if v.get("price")]
6 rows.append({
7 "product_id": product["id"],
8 "title": product["title"],
9 "vendor": product.get("vendor", ""),
10 "product_type": product.get("product_type", ""),
11 "variant_count": len(product.get("variants", [])),
12 "min_price": min(prices) if prices else None,
13 "max_price": max(prices) if prices else None,
14 "any_available": any(v.get("available", False) for v in product.get("variants", [])),
15 "tags": ", ".join(product.get("tags", []))
16 })
17 return rows
ייצוא ל‑CSV, Excel ו‑Google Sheets
1# CSV export (use utf-8-sig so Excel handles special characters)
2df.to_csv("shopify_products.csv", index=False, encoding="utf-8-sig")
3# Excel export (requires openpyxl)
4df.to_excel("shopify_products.xlsx", index=False, engine="openpyxl")
5print("Exported to shopify_products.csv and shopify_products.xlsx")
ל‑Google Sheets אפשר להשתמש בספריית gspread עם service account, אבל בכנות — לרוב המקרים, ייצוא ל‑CSV והעלאה ל‑Google Drive הם פתרון מהיר ופשוט יותר.
גרידת Shopify מוכנה לפרודקשן: מגבלות קצב, ניסיונות חוזרים והתחמקות מחסימות
הסקריפט הבסיסי עובד מצוין בחנויות קטנות. אבל כשמגרדים חנות עם יותר מ‑5,000 מוצרים, או כמה חנויות ברצף? שם מתחילות הבעיות.
איך Shopify מתנהגת מבחינת מגבלות קצב וחסימות
לנקודות הקצה הציבוריות של JSON ב‑Shopify אין מגבלות קצב מתועדות רשמית (בניגוד למודל leaky bucket של ה‑Admin API), אבל בדיקות בפועל מראות:
- קצב בטוח: בערך 2 בקשות לשנייה לכל חנות
- תקרה רכה: כ‑40 בקשות בדקה לפני שמתחיל throttling
- HTTP 429: "Too Many Requests" — תשובת מגבלת הקצב הסטנדרטית
- HTTP 430: קוד ייעודי ל‑Shopify שמצביע על חסימה ברמת אבטחה, לא רק מגבלת קצב
- HTTP 403 או הפניה ל‑CAPTCHA: בחלק מהחנויות עם הגנת Cloudflare נוספת
בקשות שמגיעות מתשתיות ענן משותפות (AWS Lambda, Google Cloud Run) נוטות במיוחד להיחסם, כי טווחי ה‑IP האלה מזוהים עם שיעורי שימוש לרעה גבוהים.
טכניקות לגרידת Shopify בצורה אמינה
זו ההתקדמות מ‑"עובד על המחשב שלי" ל‑"עובד בפרודקשן":
| רמה | טכניקה | אמינות |
|---|---|---|
| בסיסית | requests.get() + ?page= | נשבר בקטלוגים גדולים, עלול להיחסם |
| בינונית | requests.Session() + ?limit=250 + time.sleep(1) + retry על 429 | עובד ברוב החנויות |
| מתקדמת | httpx אסינכרוני + User-Agent מתחלף + exponential backoff | ברמת פרודקשן, מתאים ל‑10K+ מוצרים |
רמה בינונית (מומלצת לרוב המשתמשים):
1import requests
2from requests.adapters import HTTPAdapter
3from urllib3.util.retry import Retry
4def create_session():
5 """Create a requests session with automatic retry logic."""
6 session = requests.Session()
7 retries = Retry(
8 total=5,
9 backoff_factor=1, # sleep: 0.5s, 1s, 2s, 4s, 8s
10 status_forcelist=[429, 430, 500, 502, 503, 504],
11 respect_retry_after_header=True
12 )
13 session.mount("https://", HTTPAdapter(max_retries=retries))
14 session.headers.update({
15 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"
16 })
17 return session
הגדרת ה‑Retry מטפלת אוטומטית בתגובות 429 בעזרת exponential backoff. הערך backoff_factor=1 אומר שסדרת ההמתנה תהיה 0.5 שניות → 1 שנייה → 2 שניות → 4 שניות → 8 שניות בין ניסיונות חוזרים. שימוש חוזר ב‑Session (requests.Session()) גם מעניק pooling של חיבורים, מה שמפחית overhead כשמבצעים כמה בקשות לאותו דומיין.
סיבוב User-Agent: אם אתם מגרדים כמה חנויות, רצוי להחליף בין 3–5 מחרוזות User-Agent ריאליסטיות של דפדפנים. זה לא עניין של הטעיה — זה פשוט מונע מכם להיראות כמו בוט ששולח בדיוק אותן כותרות בכל בקשה.
סקריפט Python מלא לגרידת Shopify עם ייצוא ל‑CSV
הנה הסקריפט המלא, מוכן להעתקה ולהדבקה, שמחבר את כל מה שראינו עד עכשיו. הוא כולל בערך 75 שורות קוד בפועל (ועוד הערות), ונבדק מול Allbirds (1,420 מוצרים), ColourPop (יותר מ‑2,000 מוצרים) ו‑Zoologist Perfumes (קטלוג קטן).
1import requests
2import pandas as pd
3import time
4import random
5from requests.adapters import HTTPAdapter
6from urllib3.util.retry import Retry
7def create_session():
8 """Create a session with retry logic for rate limits."""
9 session = requests.Session()
10 retries = Retry(
11 total=5,
12 backoff_factor=1,
13 status_forcelist=[429, 430, 500, 502, 503, 504],
14 respect_retry_after_header=True
15 )
16 session.mount("https://", HTTPAdapter(max_retries=retries))
17 session.headers.update({
18 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
19 "AppleWebKit/537.36 (KHTML, like Gecko) "
20 "Chrome/125.0.0.0 Safari/537.36"
21 })
22 return session
23def scrape_shopify(store_url, delay=1.0):
24 """Scrape all products from a Shopify store via /products.json."""
25 session = create_session()
26 all_products = []
27 page = 1
28 base_url = f"{store_url.rstrip('/')}/products.json"
29 while True:
30 print(f" Page {page}...", end=" ")
31 resp = session.get(base_url, params={"limit": 250, "page": page}, timeout=30)
32 resp.raise_for_status()
33 products = resp.json().get("products", [])
34 if not products:
35 break
36 all_products.extend(products)
37 print(f"{len(products)} products (total: {len(all_products)})")
38 page += 1
39 time.sleep(delay + random.uniform(0, 0.5))
40 return all_products
41def flatten_to_variants(products):
42 """Flatten nested product JSON into one row per variant."""
43 rows = []
44 for p in products:
45 base = {
46 "product_id": p["id"],
47 "title": p["title"],
48 "handle": p["handle"],
49 "vendor": p.get("vendor", ""),
50 "product_type": p.get("product_type", ""),
51 "tags": ", ".join(p.get("tags", [])),
52 "created_at": p.get("created_at", ""),
53 "updated_at": p.get("updated_at", ""),
54 "image_url": p["images"][0]["src"] if p.get("images") else "",
55 }
56 for v in p.get("variants", []):
57 row = {**base}
58 row["variant_id"] = v["id"]
59 row["variant_title"] = v.get("title", "")
60 row["sku"] = v.get("sku", "")
61 row["price"] = v.get("price", "")
62 row["compare_at_price"] = v.get("compare_at_price", "")
63 row["available"] = v.get("available", "")
64 rows.append(row)
65 return rows
66if __name__ == "__main__":
67 STORE_URL = "https://allbirds.com" # Change this to your target store
68 OUTPUT_CSV = "shopify_products.csv"
69 OUTPUT_EXCEL = "shopify_products.xlsx"
70 print(f"Scraping {STORE_URL}...")
71 products = scrape_shopify(STORE_URL)
72 print(f"\nTotal products scraped: {len(products)}")
73 print("Flattening to variant-level rows...")
74 rows = flatten_to_variants(products)
75 df = pd.DataFrame(rows)
76 print(f"DataFrame: {df.shape[0]} rows x {df.shape[1]} columns")
77 df.to_csv(OUTPUT_CSV, index=False, encoding="utf-8-sig")
78 df.to_excel(OUTPUT_EXCEL, index=False, engine="openpyxl")
79 print(f"\nExported to {OUTPUT_CSV} and {OUTPUT_EXCEL}")
מריצים אותו עם python scrape_shopify.py. עבור Allbirds, זה לוקח בערך 45 שניות ומפיק CSV עם כ‑5,000+ שורות (שורה לכל וריאנט). הפלט בטרמינל ייראה בערך כך:
1Scraping https://allbirds.com...
2 Page 1... 250 products (total: 250)
3 Page 2... 250 products (total: 500)
4 ...
5 Page 6... 170 products (total: 1420)
6Total products scraped: 1420
7Flattening to variant-level rows...
8DataFrame: 5680 rows x 14 columns
9Exported to shopify_products.csv and shopify_products.xlsx
דלגו על Python: גרידת Shopify בשתי קליקים עם Thunderbit (חלופה ללא קוד)
לא כולם רוצים להתקין Python, לפתור שגיאות ייבוא או לתחזק סקריפט גרידה. עבור איש מכירות שצריך מחירי מתחרים עד מחר בבוקר, Python הוא פשוט יותר מדי.
לכן בנינו את — web scraper מבוסס AI שעובד כתוסף Chrome. בלי קוד, בלי מפתחות API, בלי צורך בהגדרת סביבה.
איך Thunderbit מגרד חנויות Shopify
ל‑Thunderbit יש תבנית Shopify Scraper ייעודית ומוגדרת מראש לעמודי מוצר ב‑Shopify. מתקינים את , נכנסים לחנות Shopify ולוחצים "Scrape". התבנית מחלצת אוטומטית שמות מוצרים, תיאורים, מחירים, פרטי וריאנטים, תמונות ומידע על ספקים.
בחנויות שבהן התבנית לא תואמת בצורה מושלמת (תבניות מותאמות אישית, פריסה חריגה), יכולת AI Suggest Fields של Thunderbit קוראת את הדף ומייצרת שמות עמודות אוטומטית. אפשר להתאים את זה — לשנות שמות עמודות, להוסיף שדות, ולכתוב הוראות כמו "לחלץ רק מוצרים עם compare_at_price מוגדר."
כמה יכולות שממפות ישירות למה שהסקריפט ב‑Python עושה:
- גרידת תתי‑עמודים: נכנסת אוטומטית לכל עמוד מוצר ומעשירה את הטבלה בתיאורים מלאים, ביקורות או פרטי וריאנטים — בדיוק כמו שהסקריפט שלנו עושה דרך מעבר בין עמודים, אבל בלי קוד.
- pagination אוטומטי: מטפל בעמודים עם לחיצה או בגלילה אינסופית בלי צורך בהגדרה.
- גרידה מתוזמנת: אפשר להגדיר משימות חוזרות (למשל "כל יום שני ב‑9 בבוקר") למעקב מחירים שוטף — בלי cron job ובלי שרת.
- ייצוא חינמי ל‑CSV, Excel, Google Sheets, Airtable או Notion בכל התוכניות.
סקריפט Python מול Thunderbit: השוואה כנה
| גורם | סקריפט Python | Thunderbit (ללא קוד) |
|---|---|---|
| זמן התקנה | 15–60 דק' (סביבה + קוד) | כ‑2 דק' (התקנת תוסף Chrome) |
| צריך קוד? | כן (Python) | לא |
| התאמה אישית | בלתי מוגבלת | שדות מוצעים ב‑AI + פרומפטים מותאמים |
| טיפול ב‑pagination | צריך לכתוב ידנית | אוטומטי |
| פורמטי ייצוא | צריך לממש לבד (CSV/Excel) | CSV, Excel, Google Sheets, Airtable, Notion (חינם) |
| הרצות מתוזמנות | cron + hosting | מתזמן מובנה |
| טיפול במגבלות קצב | צריך לכתוב retries/backoff | מטופל אוטומטית |
| הכי מתאים ל | מפתחים, צינורות נתונים גדולים | משתמשים עסקיים, חילוץ מהיר, ניטור חוזר |
בחרו ב‑Python כשאתם צריכים שליטה מלאה או משתלבים בצינור נתונים רחב יותר. בחרו ב‑Thunderbit כשאתם צריכים נתונים מהר ולא רוצים לתחזק קוד. לעיון מעמיק יותר ב‑, כתבנו מדריך נפרד בנושא.

טיפים ושיטות עבודה מומלצות לגרידת חנויות Shopify
אלו תקפים בלי קשר לכלי שבו תבחרו:
- תמיד השתמשו ב‑
?limit=250כדי לצמצם את מספר הבקשות. ברירת המחדל של 30 לעמוד פירושה פי 8 יותר בקשות לאותו מידע. - כבדו את החנות: הוסיפו השהייה של 1–2 שניות בין בקשות. הפגזת שרת בבקשות מהירות היא גם לא נימוסית וגם מעלה את הסיכוי להיחסם.
- בדקו קודם את
robots.txt: כברירת מחדל, Shopify לא חוסמת את/products.jsonב‑robots.txt. אבל חלק מהחנויות מוסיפות חוקים מותאמים, אז כדאי לוודא לפני גרידה בקנה מידה גדול. - שמרו קודם את ה‑JSON הגולמי מקומית, ורק אחר כך תעבדו אותו. אם ההיגיון של הניתוח ישתנה בהמשך, לא תצטרכו לגרד מחדש.
json.dump(all_products, open("raw_data.json", "w"))פשוט לפני השטחה יחסוך כאב ראש. - בצעו deduplication לפי
product.id: במקרי קצה של pagination עלולות להופיע כפילויות בגבולות בין עמודים.df.drop_duplicates(subset=["product_id", "variant_id"])יפתור את זה מהר. - המירו
priceל‑float לפני חישובים. Shopify מחזירה מחירים כמחרוזות ("185.00"), לא כמספרים. - עקבו אחרי שינויים בנקודת הקצה: אמנם
/products.jsonיציבה כבר שנים, אבל תיאורטית Shopify יכולה להגביל אותה. אם הסקריפט פתאום מתחיל להחזיר 404, בדקו קודם את החנות ידנית.
לעוד מידע על בניית סקרייפרים יציבים, ראו את המדריך שלנו על .
שיקולים משפטיים ואתיים בגרידת Shopify
קטע קצר, אבל חשוב.
נקודת הקצה /products.json מגישה נתוני מוצרים הזמינים לציבור — אותו מידע שכל מבקר רואה כשגולשים בחנות. בתנאי השירות של Shopify יש ניסוח לגבי אי‑שימוש ב"אמצעים אוטומטיים" כדי לגשת ל"שירותים", אבל הניסוח הזה מתייחס לפלטפורמה עצמה (לוח הניהול, ה‑checkout), לא לנתוני חנות ציבוריים. נכון לאפריל 2026, לא הוגשו תביעות גרידה ספציפיות ל‑Shopify.
תקדימים משפטיים חשובים תומכים בגרידת נתונים ציבוריים: hiQ v. LinkedIn קבע שגרידת מידע הזמין לציבור אינה מפרה את ה‑CFAA, ו‑Meta v. Bright Data (2024) קבע שהגבלות TOS חלות רק כאשר המשתמש מחובר.
שיטות עבודה מומלצות:
- גרדו רק נתוני מוצרים ציבוריים
- אל תגרדו נתונים אישיים או נתוני לקוחות
- כבדו
robots.txtומגבלות קצב - עמדו ב‑GDPR/CCPA אם אתם מטפלים בנתונים אישיים כלשהם (נתוני קטלוג מוצרים אינם אישיים)
- הזדהו עם מחרוזת User-Agent ברורה
- גרידה של חנות Shopify שלכם דרך ה‑Admin API תמיד תקינה
לקריאה מעמיקה יותר, ראו את הפוסט שלנו על .
מסקנות וסיכום עיקרי
נקודת הקצה הציבורית /products.json של Shopify הופכת חילוץ נתוני איקומרס לפשוט מאוד. התהליך הוא: מוסיפים /products.json → שולפים עם Python → עוברים על pagination עם ?limit=250&page= → משטחים עם pandas → מייצאים ל‑CSV או Excel.
מה שהמדריך הזה מכסה ושאחרים לא:
- פירוט מלא של שדות: יודעים בדיוק אילו נתונים זמינים (40+ שדות על פני מוצרים, וריאנטים ותמונות) לפני כתיבת שורת קוד אחת
- נקודות קצה נוספות:
/collections.jsonו‑/meta.jsonנותנות לכם מודיעין ברמת קטגוריה ומטא־נתוני חנות שאף מדריך מתחרה לא מכסה - טכניקות מוכנות לפרודקשן: שימוש חוזר ב‑Session, exponential backoff, כותרות User-Agent ו‑
?limit=250כדי להתמודד עם מגבלות קצב אמיתיות - ייצוא נכון ל‑CSV/Excel: נתונים שטוחים ברמת וריאנט עם pandas, לא רק dump גולמי של JSON
- חלופה ללא קוד: למי שמעדיף מהירות על פני גמישות קוד
למשיכות נתוני Shopify חד‑פעמיות או חוזרות בלי קוד, נסו את — תבנית Shopify Scraper מטפלת בהכול, מה‑pagination ועד הייצוא. עבור צינורות נתונים מותאמים אישית או גרידה בקנה מידה רחב על פני חנויות רבות, סקריפט ה‑Python במדריך הזה נותן לכם שליטה מלאה.
בדקו את להדרכות וידאו, או עיינו במדריכים שלנו על ועל לטכניקות קשורות.
שאלות נפוצות
האם אפשר לגרד כל חנות Shopify עם products.json?
רוב חנויות Shopify חושפות את הנקודה הזו כברירת מחדל — בבדיקות, בערך 71% החזירו JSON תקין. חלק מהחנויות עם תצורות מותאמות אישית או שכבות אבטחה נוספות (Cloudflare, תצורות headless) עלולות להחזיר 404 או לחסום את הבקשה. בדיקה מהירה: נכנסים ל‑{store-url}/products.json בדפדפן. אם רואים JSON, הכול תקין.
האם חוקי לגרד חנויות Shopify?
נתוני מוצרים ציבוריים (מחירים, כותרות, תמונות, תיאורים) בדרך כלל נגישים, ותקדימים משפטיים כמו hiQ v. LinkedIn תומכים בגרידה של מידע ציבורי. עם זאת, תמיד כדאי לבדוק את תנאי השירות של החנות הספציפית ואת החוקים המקומיים. אל תגרדו נתונים אישיים או נתוני לקוחות, וכבדו מגבלות קצב.
כמה מוצרים אפשר לגרד מחנות Shopify?
אין מגבלה קשיחה על הכמות הכוללת. pagination עם ?limit=250&page= מאפשר למשוך את כל הקטלוג. בחנויות גדולות מאוד (25,000+ מוצרים), כדאי להשתמש ב‑Session reuse ובהשהיות כדי להימנע ממגבלות קצב. נקודת הקצה /meta.json יכולה להגיד לכם מראש מה מספר המוצרים המדויק, כך שתדעו כמה עמודים לצפות.
מה ההבדל בין products.json לבין Shopify Admin API?
/products.json היא נקודת קצה ציבורית — בלי אימות, נתוני מוצר לקריאה בלבד, זמינים לכל אחד. ה‑Admin API דורש אסימוני גישה של בעל החנות ומספק הזמנות, כמויות מלאי, נתוני לקוחות והרשאת כתיבה. אם אתם צריכים נתוני מכירות או ספירות מלאי אמיתיות, תצטרכו גישה ל‑Admin API (כלומר להיות בעלי החנות או לפעול בהרשאתם).
אפשר לגרד Shopify בלי Python?
בהחלט. כלים כמו מאפשרים לגרד חנויות Shopify מתוך תוסף Chrome בלי לכתוב קוד. הוא מטפל ב‑pagination אוטומטית ומייצא ישירות ל‑CSV, Excel, Google Sheets, Airtable או Notion. עבור מפתחים שמעדיפים שפות אחרות, אותה נקודת קצה /products.json עובדת גם עם JavaScript, Ruby, Go — כל שפה שיכולה לבצע בקשות HTTP ולנתח JSON.
למידע נוסף