Shopifys /products.json-Endpunkt gehört zu den am besten gehüteten Geheimnissen im E-Commerce-Datenbereich. Hängen Sie ihn an eine beliebige Shopify-Store-URL an, und schon bekommen Sie strukturiertes JSON zurück, ohne API-Key, ohne Authentifizierung und ohne sich durch verschachteltes HTML zu kämpfen.
Ich arbeite im Team der Thunderbit Official Website, beschäftige mich also täglich damit, wie Menschen Daten aus dem Web ziehen. Und Shopify-Scraping taucht dabei immer wieder auf – von Sales-Teams, die Konkurrenzpreise im Auge behalten, über E-Commerce-Operations, die Produktkataloge vergleichen, bis zu Einkaufsteams auf Lieferantensuche. Bei 4,82 Millionen aktiven Händlern auf Shopify und einem Marktanteil von rund 30 % im US-E-Commerce liegt eine gewaltige Menge an Produktdaten zum Auslesen bereit.
Dieser Leitfaden deckt den kompletten Ablauf ab: was der Endpunkt zurückgibt, wie Sie Tausende Produkte per Paginierung abrufen, wie Sie ohne Blockierungen mit Rate Limits umgehen und wie Sie Shopifys verschachteltes JSON mit pandas in eine saubere CSV- oder Excel-Datei verwandeln. Außerdem zeige ich die Endpunkte, über die sonst kaum jemand spricht (/collections.json, /meta.json), und eine No-Code-Alternative für alle, die Python komplett umgehen wollen.
Was ist Shopifys /products.json-Endpunkt – und warum macht er Scraping so einfach?
Jeder Shopify-Store hat einen öffentlichen Endpunkt unter {store-url}/products.json, der strukturierte Produktdaten zurückliefert. Keine API-Keys. Kein OAuth. Keine Authentifizierung irgendeiner Art. Sie hängen /products.json an die Store-URL an und erhalten ein JSON-Array mit allen Produkten aus dem Katalog.
Probieren Sie es direkt aus: Öffnen Sie allbirds.com/products.json oder zoologistperfumes.com/products.json in Ihrem Browser. Sie sehen sauberes, strukturiertes JSON mit Produkttiteln, Preisen, Varianten, Bildern und Tags.
Die Alternative ist deutlich mühsamer: Shopifys HTML-Themes parsen. Die sind tief verschachtelt, von Store zu Store uneinheitlich und ändern sich, sobald ein Händler sein Theme aktualisiert. So sähe das aus:
HTML-Ansatz (unschön):
<div class="product-card__info">
<h3 class="product-card__title">
<a href="/products/classic-blue-jeans">Classic Blue Jeans</a>
</h3>
<span class="price price--on-sale" data-product-price>$149.00</span>
</div>
JSON-Ansatz (sauber):
{
"title": "Classic Blue Jeans",
"handle": "classic-blue-jeans",
"vendor": "Hiut Denim",
"variants": [{"price": "149.00", "sku": "HD-BLU-32", "available": true}]
}
JSON gewinnt bei Konsistenz, Zuverlässigkeit und einfacher Weiterverarbeitung. Der Endpunkt unterstützt außerdem zwei wichtige Query-Parameter – ?limit= (bis zu 250 Produkte pro Seite, Standard sind 30) und ?page= für die Paginierung – die wir im Code unten ausgiebig nutzen.
Wichtige Abgrenzung: Das hier ist ein öffentlicher Storefront-Endpunkt, nicht die Shopify Admin API. Die Admin API braucht Zugriffstoken des Store-Besitzers und liefert Bestelldaten, Lagerbestände und Kundendaten. Der öffentliche /products.json-Endpunkt enthält nur schreibgeschützte Produktdaten, auf die jeder zugreifen kann. Auf diesen Unterschied komme ich später noch genauer ein, weil er in Foren ständig für Verwirrung sorgt.
Wichtig: Nicht jeder Shopify-Store stellt diesen Endpunkt bereit. In meinen Tests lieferten etwa 71 % der Stores gültiges JSON (allbirds.com, gymshark.com, colourpop.com, kyliecosmetics.com funktionierten alle), während einige individuelle Setups 404 zurückgaben (hiutdenim.co.uk, bombas.com). Der Schnellcheck ist denkbar einfach: Besuchen Sie {store-url}/products.json im Browser und schauen Sie, was zurückkommt.
Warum Shopify mit Python scrapen? Die wichtigsten Business-Anwendungsfälle
Warum sich der Aufwand lohnt? Wegen des ROI. 81 % der US-Händler nutzen inzwischen automatisiertes Preisscraping für Competitive Intelligence – 2020 waren es erst 34 %. Und Studien zeigen, dass eine Verbesserung der Preisstrategie um nur 1 % im Schnitt zu 11,1 % mehr Gewinn führt. Diese Daten sind also bares Geld wert.
Diese Anwendungsfälle sehe ich am häufigsten:
| Anwendungsfall | Wer profitiert | Was Sie erhalten |
|---|---|---|
| Konkurrenzpreis-Monitoring | E-Commerce-Operations-Teams | Preisänderungen, Rabatte und Vergleichspreise in Konkurrenzkatalogen nachverfolgen |
| Produktrecherche & Beschaffung | Einkauf / Merchandising | Produktmerkmale, Varianten, Materialien und Verfügbarkeit vergleichen |
| Lead-Generierung | Vertriebsteams | Lieferantennamen, Markendaten und Kontaktinformationen aus Store-Katalogen extrahieren |
| Markt- & Kategorieanalyse | Marketing-Teams | Produktmix, Tags, Kategoriestruktur und Positionierung verstehen |
| Lager- & Verfügbarkeits-Tracking | Supply-Chain-Teams | Variantenbezogenen Lagerstatus (available: true/false) über die Zeit beobachten |
| Erkennung neuer Produkte | Produktteams | created_at-Zeitstempel verfolgen, um neue Launches von Wettbewerbern zu erkennen |
Python ist dafür die naheliegende Wahl. 69,6 % der Entwickler im Scraping-Bereich nutzen Python als primäre Sprache, und das Ökosystem – requests für HTTP, pandas für Datenverarbeitung, httpx für Async – bringt Sie in unter 80 Codezeilen von „Ich habe eine URL“ zu „Ich habe eine Tabelle“.
Vollständige Feldübersicht für products.json: Jedes Feld erklärt
Jedes Konkurrenz-Tutorial zeigt Ihnen title, id und handle und macht dann weiter. Die JSON-Antwort von Shopify enthält aber über 40 Felder über Produkte, Varianten, Bilder und Optionen hinweg. Wenn Sie vor dem Coden wissen, was verfügbar ist, ersparen Sie sich späteres erneutes Scrapen.
Diese Referenz habe ich aus Live-Antworten von /products.json zusammengestellt, die am 16. April 2026 abgerufen wurden. Die Struktur ist bei allen Stores, die den Endpunkt bereitstellen, konsistent.
Felder auf Produktebene
| Feld | Datentyp | Beispielwert | Business-Nutzen |
|---|---|---|---|
id | Integer | 123456789 | Eindeutige Produkt-ID zur Duplikatvermeidung |
title | String | "Classic Blue Jeans" | Produktname für Kataloge und Vergleiche |
handle | String | "classic-blue-jeans" | URL-Slug – Produktlinks als {store}/products/{handle} aufbauen |
body_html | String (HTML) oder null | Our best-selling... | Produktbeschreibung für Inhaltsanalyse und SEO-Recherche |
vendor | String | "Hiut Denim" | Marken-/Lieferantenname für Lead-Gen oder Beschaffung |
product_type | String | "Jeans" | Kategorisierung für Marktanalysen |
created_at | ISO-DateTime | "2024-01-15T10:30:00-05:00" | Nachverfolgen, wann Produkte hinzugefügt wurden (Neuer-Launch-Erkennung) |
updated_at | ISO-DateTime | "2025-03-01T08:00:00-05:00" | Kürzliche Katalogänderungen erkennen |
published_at | ISO-DateTime | "2024-01-16T00:00:00-05:00" | Wissen, wann Produkte im Store live gingen |
tags | Array von Strings | ["organic", "women", "straight-leg"] | Keyword-/Tag-Analyse für SEO, Kategorisierung und Trendbeobachtung |
variants | Array von Objekten | (siehe Variantenfelder unten) | Preis, SKU und Verfügbarkeit je Variante |
images | Array von Objekten | (siehe Bildfelder unten) | Bild-URLs für Kataloge und visuelle Analysen |
options | Array von Objekten | [{"name": "Size", "values": ["S","M","L"]}] | Produktkonfiguration verstehen (Größe, Farbe, Material) |
Felder auf Variantenebene (verschachtelt unter jedem Produkt)
| Feld | Datentyp | Beispiel | Use Case |
|---|---|---|---|
id | Integer | 987654321 | Eindeutige Varianten-ID |
title | String | "32 / Blue" | Anzeigename der Variante |
sku | String | "HD-BLU-32" | SKU-Abgleich für Inventarsysteme |
price | String | "185.00" | Preis-Monitoring (Achtung: String, für Berechnungen in Float umwandeln) |
compare_at_price | String oder null | "200.00" | Originalpreis – wichtig für Rabattverfolgung |
available | Boolean | true | Lagerverfügbarkeit (der einzige öffentliche Lagerindikator) |
weight | Float | 1.2 | Analyse für Versand und Logistik |
option1, option2, option3 | String | "32", "Blue", null | Einzelne Optionswerte |
created_at, updated_at | ISO-DateTime | — | Änderungsverfolgung auf Variantenebene |
Felder auf Bildebene
| Feld | Datentyp | Beispiel | Use Case |
|---|---|---|---|
id | Integer | 111222333 | Eindeutige Bild-ID |
src | String (URL) | "https://cdn.shopify.com/..." | Direkter Download-Link zum Bild |
alt | String oder null | "Front view of jeans" | Alt-Text für Barrierefreiheitsanalyse |
position | Integer | 1 | Reihenfolge der Bilder |
width, height | Integer | 2048, 2048 | Bildabmessungen |
Was im öffentlichen Endpunkt NICHT enthalten ist
Ein wichtiger Stolperstein: inventory_quantity ist in öffentlichen /products.json-Antworten NICHT verfügbar. Dieses Feld wurde im Dezember 2017 aus Sicherheitsgründen aus den öffentlichen JSON-Endpunkten entfernt. Der einzige Lagerhinweis, den Sie bekommen, ist das boolesche Feld available bei jeder Variante (true oder false). Um echte Bestandsmengen abzurufen, brauchen Sie die authentifizierte Admin API mit den Zugangsdaten des Store-Besitzers.
Bevor Sie mit dem Scraping beginnen, gehen Sie diese Tabelle durch und entscheiden Sie, welche Felder für Ihren Use Case zählen. Wer Preise überwacht, braucht variants[].price, variants[].compare_at_price und variants[].available. Wer Leads generiert, konzentriert sich auf vendor, product_type und tags. Filtern Sie entsprechend – Ihre CSV wird dadurch deutlich sauberer.
Über products.json hinaus: Collections, Meta und weitere Shopify-Endpunkte
Kein Konkurrenzleitfaden erwähnt diese Endpunkte. Für ernsthafte Competitive-Intelligence-Arbeit sind sie aber Gold wert.
/collections.json – Alle Kategorien des Stores
Gibt alle Collections (Kategorien) des Stores mit Titeln, Handles, Beschreibungen und Produktanzahlen zurück. Ich habe das auf zoologistperfumes.com, allbirds.com und gymshark.com geprüft – alle lieferten gültiges JSON.
{
"collections": [
{
"id": 308387348539,
"title": "Attars",
"handle": "attars",
"published_at": "2026-03-29T12:20:32-04:00",
"products_count": 1,
"image": { "src": "https://cdn.shopify.com/..." }
}
]
}
Sie wollen verstehen, wie ein Wettbewerber seinen Katalog strukturiert? Dann ist das der richtige Endpunkt.
/collections/{handle}/products.json – Produkte nach Kategorie
Liefert Produkte, gefiltert nach einer bestimmten Collection. Gleiche JSON-Struktur wie /products.json, aber nur für eine Kategorie. Das ist besonders wichtig für Scraping auf Kategorieebene – etwa, wenn Sie nur die „Sale“- oder „New Arrivals“-Kollektion eines Wettbewerbers beobachten wollen.
/meta.json – Metadaten auf Store-Ebene
Gibt Store-Namen, Beschreibung, Währung, Land und published_products_count zurück. Damit berechnen Sie im Voraus genau, wie viele Seiten Sie für die Paginierung brauchen: ceil(published_products_count / 250). Kein endloses Hochzählen mehr, bis eine leere Antwort kommt.
Welchen Endpunkt sollten Sie verwenden?
| Was Sie möchten | Endpunkt | Authentifizierung nötig? |
|---|---|---|
| Alle Produkte (öffentlich) | /products.json | Nein |
| Produkte in einer bestimmten Kategorie | /collections/{handle}/products.json | Nein |
| Store-Metadaten + Produktanzahl | /meta.json | Nein |
| Alle Collections (Kategorien) | /collections.json | Nein |
| Bestell-/Verkaufsdaten (nur eigener Store) | Admin API /orders.json | Ja (API-Key) |
| Lagerbestände (nur eigener Store) | Admin API /inventory_levels.json | Ja |
Die immer wieder auftauchende Forenfrage – „Kann ich sehen, wie viele Einheiten ein Konkurrent verkauft hat?“ – hat eine kurze Antwort: nein. Nicht über öffentliche Endpunkte. Verkaufsdaten und Bestandsmengen brauchen die authentifizierte Admin API, also Zugriff des Store-Besitzers.

Shopify mit Python scrapen: Schritt-für-Schritt-Einrichtung
- Schwierigkeit: Anfänger
- Zeitaufwand: ca. 15 Minuten (Setup + erster Scrape)
- Was Sie brauchen: Python 3.11+,
pip, ein Terminal und eine Shopify-Store-URL zum Scrapen
Schritt 1: Python und benötigte Bibliotheken installieren
Stellen Sie sicher, dass Python 3.11 oder neuer installiert ist (pandas 3.0.x setzt das voraus). Installieren Sie dann die zwei Bibliotheken, die wir brauchen:
pip install requests pandas
Für den Excel-Export benötigen Sie zusätzlich:
pip install openpyxl
Fügen Sie am Anfang Ihres Skripts diese Imports ein:
import requests
import pandas as pd
import time
import random
import json
Beim Ausführen des Skripts sollten keine Importfehler auftreten. Meldet pandas einen Versionsfehler, aktualisieren Sie Python auf 3.12.
Schritt 2: Produktdaten aus /products.json abrufen
Hier ist eine einfache Funktion, die eine Store-URL annimmt, den Endpunkt anspricht und das geparste JSON zurückgibt:
def fetch_products_page(store_url, page=1, limit=250):
"""Eine einzelne Produktseite aus einem Shopify-Store abrufen."""
url = f"{store_url.rstrip('/')}/products.json"
params = {"limit": limit, "page": page}
headers = {
"User-Agent": "Mozilla/5.0 (compatible; ProductResearch/1.0)"
}
response = requests.get(url, params=params, headers=headers, timeout=30)
response.raise_for_status()
return response.json().get("products", [])
Wichtige Details:
limit=250ist das Maximum, das Shopify pro Seite zulässt. Der Standardwert ist 30, daher reduzieren Sie mit dieser expliziten Einstellung die Anzahl der Requests um bis zu das Achtfache.User-Agent-Header: Verwenden Sie immer einen realistischen Wert. Requests ohne User-Agent lösen eher die Anti-Bot-Systeme von Shopify aus.timeout=30: Lassen Sie keinen einzelnen Request ewig hängen.
Testen Sie die Funktion mit einem bekannten Store:
products = fetch_products_page("https://allbirds.com")
print(f"Fetched {len(products)} products")
print(f"First product: {products[0]['title']}")
Sie sollten etwa Folgendes sehen: Fetched 250 products und den Titel des ersten Produkts.
Schritt 3: Paginierung nutzen, um alle Produkte zu scrapen
Ein einzelner Request liefert maximal 250 Produkte. Die meisten Stores haben mehr (Allbirds hat über 1.420). Sie müssen also durch die Seiten laufen, bis eine leere Antwort zurückkommt.
def scrape_all_products(store_url, delay=1.0):
"""Alle Produkte aus einem Shopify-Store scrapen, inklusive Paginierung."""
all_products = []
page = 1
while True:
print(f"Fetching page {page}...")
products = fetch_products_page(store_url, page=page, limit=250)
if not products:
print(f"No more products. Total: {len(all_products)}")
break
all_products.extend(products)
print(f" Got {len(products)} products (total so far: {len(all_products)})")
page += 1
# Fair bleiben: zwischen den Requests warten
time.sleep(delay + random.uniform(0, 0.5))
return all_products
Kommt products leer zurück, sind Sie am Ende angekommen.
Das time.sleep() mit zufälligem Jitter hält Sie unter Shopifys informellem Rate Limit von etwa 2 Requests pro Sekunde.
Profi-Tipp: Wer vorher /meta.json abgerufen hat, kennt die Gesamtzahl der Produkte schon und kann exakt berechnen, wie viele Seiten nötig sind: pages = ceil(product_count / 250). So sparen Sie sich den einen unnötigen leeren Request am Ende.
Schritt 4: Die benötigten Felder extrahieren und auswählen
Sobald Sie alle Produkte als Python-Liste von Dictionaries haben, ziehen Sie nur die Felder heraus, die für Sie relevant sind. Hier ein Beispiel fürs Preis-Monitoring mit den wichtigsten Feldern:
def extract_product_data(products):
"""Wichtige Felder aus Produkten extrahieren und Varianten flach darstellen."""
rows = []
for product in products:
for variant in product.get("variants", []):
rows.append({
"product_id": product["id"],
"title": product["title"],
"handle": product["handle"],
"vendor": product.get("vendor", ""),
"product_type": product.get("product_type", ""),
"tags": ", ".join(product.get("tags", [])),
"created_at": product.get("created_at", ""),
"variant_id": variant["id"],
"variant_title": variant.get("title", ""),
"sku": variant.get("sku", ""),
"price": variant.get("price", ""),
"compare_at_price": variant.get("compare_at_price", ""),
"available": variant.get("available", ""),
"image_url": product["images"][0]["src"] if product.get("images") else ""
})
return rows
Dadurch entsteht eine Zeile pro Variante – das nützlichste Format für Preisvergleiche, da ein einzelnes Produkt wie „Classic Blue Jeans“ 12 Varianten haben kann (6 Größen × 2 Farben), jede mit eigenem Preis und Verfügbarkeitsstatus.
Shopify-Daten mit pandas als CSV und Excel exportieren
Jeder andere Shopify-Scraping-Leitfaden speichert einfach das rohe JSON und nennt es fertig. Für Entwickler okay. Für Analysten im E-Commerce, die bis Freitag eine Tabelle brauchen, reicht das nicht.
Das Problem: Shopifys JSON ist verschachtelt. Ein Produkt kann ein Dutzend Varianten enthalten, jede mit eigenem Preis, SKU und Verfügbarkeit. Das in Zeilen und Spalten zu bringen, braucht etwas pandas-Arbeit.
Verschachteltes JSON in eine saubere Tabelle überführen
Je nach Ziel gibt es zwei Ansätze:
Option A: Eine Zeile pro Variante (am besten für Preis- und Lagertracking)
# Nutzung der Funktion extract_product_data aus Schritt 4
products = scrape_all_products("https://allbirds.com")
rows = extract_product_data(products)
df = pd.DataFrame(rows)
print(f"DataFrame shape: {df.shape}")
print(df.head())
So erhalten Sie eine flache Tabelle, in der jede Zeile eine eindeutige Produkt-Varianten-Kombination ist. Ein Store mit 500 Produkten und durchschnittlich 4 Varianten pro Produkt ergibt ein DataFrame mit rund 2.000 Zeilen.
Option B: Eine Zeile pro Produktübersicht (am besten für Katalogübersichten)
def summarize_products(products):
"""Eine Zeile pro Produkt mit Minimal- und Maximalpreis über alle Varianten."""
rows = []
for product in products:
prices = [float(v["price"]) for v in product.get("variants", []) if v.get("price")]
rows.append({
"product_id": product["id"],
"title": product["title"],
"vendor": product.get("vendor", ""),
"product_type": product.get("product_type", ""),
"variant_count": len(product.get("variants", [])),
"min_price": min(prices) if prices else None,
"max_price": max(prices) if prices else None,
"any_available": any(v.get("available", False) for v in product.get("variants", [])),
"tags": ", ".join(product.get("tags", []))
})
return rows
Export nach CSV, Excel und Google Sheets
# CSV-Export (utf-8-sig verwenden, damit Excel Sonderzeichen korrekt verarbeitet)
df.to_csv("shopify_products.csv", index=False, encoding="utf-8-sig")
# Excel-Export (openpyxl erforderlich)
df.to_excel("shopify_products.xlsx", index=False, engine="openpyxl")
print("Exported to shopify_products.csv and shopify_products.xlsx")
Für Google Sheets können Sie die Bibliothek gspread mit einem Service Account nutzen, aber ehrlich gesagt: Für die meisten Anwendungsfälle ist der Export nach CSV und das Hochladen zu Google Drive schneller und einfacher.
Produktionsreifes Python-Scraping: Rate Limits, Retries und Anti-Blocking
Das einfache Skript läuft bei kleinen Stores gut. Aber was ist mit Stores mit 5.000+ Produkten oder wenn Sie mehrere Stores direkt hintereinander abfragen? Genau dann fangen die Probleme an.
Shopifys Rate Limits und Blocking-Verhalten verstehen
Shopifys öffentliche JSON-Endpunkte haben keine offiziell dokumentierten Rate Limits wie die Leaky-Bucket-Logik der Admin API, aber empirische Tests zeigen:
- Sichere Rate: etwa 2 Requests pro Sekunde und Store
- Weiche Obergrenze: ungefähr 40 Requests pro Minute, bevor Throttling einsetzt
- HTTP 429: „Too Many Requests“ – die Standardantwort bei Rate Limits
- HTTP 430: Ein Shopify-spezifischer Code, der auf eine Sicherheitsblockade hinweist, nicht nur auf Rate Limiting
- HTTP 403 oder CAPTCHA-Weiterleitung: Bei manchen Stores mit zusätzlichem Cloudflare-Schutz
Requests von gemeinsamer Cloud-Infrastruktur (AWS Lambda, Google Cloud Run) lösen besonders häufig Blockaden aus, weil diese IP-Bereiche oft missbraucht werden.
So scrapen Sie Shopify zuverlässig
Hier ist die Entwicklung von „funktioniert auf meinem Laptop“ zu „läuft produktiv“:
| Stufe | Technik | Zuverlässigkeit |
|---|---|---|
| Basis | requests.get() + ?page= | Bricht bei großen Katalogen ab, kann blockiert werden |
| Mittel | requests.Session() + ?limit=250 + time.sleep(1) + Retry bei 429 | Funktioniert für die meisten Stores |
| Fortgeschritten | Async httpx + rotierender User-Agent + exponentielles Backoff | Produktionsreif, skaliert auf 10K+ Produkte |
Mittleres Niveau (für die meisten Nutzer empfohlen):
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
def create_session():
"""Eine requests-Session mit automatischer Retry-Logik erstellen."""
session = requests.Session()
retries = Retry(
total=5,
backoff_factor=1, # sleep: 0.5s, 1s, 2s, 4s, 8s
status_forcelist=[429, 430, 500, 502, 503, 504],
respect_retry_after_header=True
)
session.mount("https://", HTTPAdapter(max_retries=retries))
session.headers.update({
"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"
})
return session
Die Retry-Konfiguration behandelt 429-Antworten automatisch mit exponentiellem Backoff. backoff_factor=1 bedeutet, dass die Wartezeiten zwischen den Versuchen 0,5 s → 1 s → 2 s → 4 s → 8 s betragen. Die Wiederverwendung der Session (requests.Session()) sorgt außerdem für Connection Pooling, was den Overhead bei mehreren Requests an dieselbe Domain reduziert.
User-Agent-Rotation: Wenn Sie mehrere Stores scrapen, wechseln Sie zwischen 3–5 realistischen Browser-User-Agents. Das dient nicht der Täuschung – sondern einfach dazu, nicht wie ein Bot zu wirken, der bei jedem Request exakt dieselben Header sendet.
Vollständiges Python-Skript zum Shopify-Scraping mit CSV-Export
Hier ist das komplette Skript zum Kopieren und Einfügen, das alles oben Gesagte zusammenführt. Es umfasst rund 75 echte Codezeilen plus Kommentare und wurde gegen Allbirds (1.420 Produkte), ColourPop (2.000+ Produkte) und Zoologist Perfumes (kleiner Katalog) getestet.
import requests
import pandas as pd
import time
import random
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
def create_session():
"""Eine Session mit Retry-Logik für Rate Limits erstellen."""
session = requests.Session()
retries = Retry(
total=5,
backoff_factor=1,
status_forcelist=[429, 430, 500, 502, 503, 504],
respect_retry_after_header=True
)
session.mount("https://", HTTPAdapter(max_retries=retries))
session.headers.update({
"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"
})
return session
def scrape_shopify(store_url, delay=1.0):
"""Alle Produkte eines Shopify-Stores über /products.json scrapen."""
session = create_session()
all_products = []
page = 1
base_url = f"{store_url.rstrip('/')}/products.json"
while True:
print(f" Page {page}...", end=" ")
resp = session.get(base_url, params={"limit": 250, "page": page}, timeout=30)
resp.raise_for_status()
products = resp.json().get("products", [])
if not products:
break
all_products.extend(products)
print(f"{len(products)} products (total: {len(all_products)})")
page += 1
time.sleep(delay + random.uniform(0, 0.5))
return all_products
def flatten_to_variants(products):
"""Verschachteltes Produkt-JSON in eine Zeile pro Variante überführen."""
rows = []
for p in products:
base = {
"product_id": p["id"],
"title": p["title"],
"handle": p["handle"],
"vendor": p.get("vendor", ""),
"product_type": p.get("product_type", ""),
"tags": ", ".join(p.get("tags", [])),
"created_at": p.get("created_at", ""),
"updated_at": p.get("updated_at", ""),
"image_url": p["images"][0]["src"] if p.get("images") else "",
}
for v in p.get("variants", []):
row = {**base}
row["variant_id"] = v["id"]
row["variant_title"] = v.get("title", "")
row["sku"] = v.get("sku", "")
row["price"] = v.get("price", "")
row["compare_at_price"] = v.get("compare_at_price", "")
row["available"] = v.get("available", "")
rows.append(row)
return rows
if __name__ == "__main__":
STORE_URL = "https://allbirds.com" # Hier den Ziel-Store eintragen
OUTPUT_CSV = "shopify_products.csv"
OUTPUT_EXCEL = "shopify_products.xlsx"
print(f"Scraping {STORE_URL}...")
products = scrape_shopify(STORE_URL)
print(f"\nTotal products scraped: {len(products)}")
print("Flattening to variant-level rows...")
rows = flatten_to_variants(products)
df = pd.DataFrame(rows)
print(f"DataFrame: {df.shape[0]} rows x {df.shape[1]} columns")
df.to_csv(OUTPUT_CSV, index=False, encoding="utf-8-sig")
df.to_excel(OUTPUT_EXCEL, index=False, engine="openpyxl")
print(f"\nExported to {OUTPUT_CSV} and {OUTPUT_EXCEL}")
Starten Sie es mit python scrape_shopify.py. Bei Allbirds dauert das etwa 45 Sekunden und erzeugt eine CSV mit rund 5.000+ Zeilen, also eine Zeile pro Variante. Die Terminalausgabe sieht ungefähr so aus:
Scraping https://allbirds.com...
Page 1... 250 products (total: 250)
Page 2... 250 products (total: 500)
...
Page 6... 170 products (total: 1420)
Total products scraped: 1420
Flattening to variant-level rows...
DataFrame: 5680 rows x 14 columns
Exported to shopify_products.csv and shopify_products.xlsx
Ohne Python: Shopify in 2 Klicks mit Thunderbit scrapen (No-Code-Alternative)
Nicht jeder möchte Python installieren, Importfehler beheben oder ein Scraping-Skript pflegen. Für den Vertriebsmitarbeiter, der morgen früh Konkurrenzpreise braucht, ist Python einfach zu viel.
Genau deshalb haben wir die Thunderbit Official Website entwickelt – einen KI-Webscraper als Chrome-Erweiterung. Kein Code, keine API-Keys, kein Setup der Entwicklungsumgebung.
Thunderbit-Chrome-Erweiterung testen
So scrapt Thunderbit Shopify-Stores
Thunderbit hat eine speziell konfigurierte Shopify Scraper-Vorlage, die für Shopify-Produktseiten vorbereitet ist. Sie installieren die Thunderbit Chrome Extension Download Page, öffnen einen Shopify-Store und klicken auf „Scrape“. Die Vorlage extrahiert automatisch Produktnamen, Beschreibungen, Preise, Variantendetails, Bilder und Anbieterinformationen.
Bei Stores, die nicht ganz zum Template passen (etwa bei benutzerdefinierten Themes oder ungewöhnlichen Layouts), liest Thunderbits Funktion AI Suggest Fields die Seite aus und schlägt automatisch Spaltennamen vor. Diese passen Sie an – Spalten umbenennen, Felder hinzufügen oder Anweisungen wie „nur Produkte mit gesetztem compare_at_price extrahieren“ geben.
Einige Funktionen, die direkt dem entsprechen, was das Python-Skript erledigt:
- Subpage-Scraping: Ruft automatisch jede Produktdetailseite auf und reichert die Tabelle mit vollständigen Beschreibungen, Rezensionen oder Variantendetails an – genau wie unser Python-Skript durch das Durchlaufen der Seiten, nur eben ohne Code.
- Automatische Paginierung: Verarbeitet Klick-Paginierung und unendliches Scrollen ohne Konfiguration.
- Geplantes Scraping: Richten Sie wiederkehrende Jobs ein (zum Beispiel „jeden Montag um 9 Uhr“) für laufendes Preis-Monitoring – ganz ohne Cronjob oder Server.
- Kostenloser Export nach CSV, Excel, Google Sheets, Airtable oder Notion in allen Plänen.
Python-Skript vs. Thunderbit: Ehrlicher Vergleich
| Faktor | Python-Skript | Thunderbit (No-Code) |
|---|---|---|
| Einrichtungszeit | 15–60 Min. (Umgebung + Code) | ca. 2 Min. (Chrome-Erweiterung installieren) |
| Programmieren erforderlich | Ja (Python) | Nein |
| Anpassbarkeit | Unbegrenzt | KI-vorgeschlagene Felder + eigene Prompts |
| Paginierung | Muss manuell programmiert werden | Automatisch |
| Exportformate | Selbst programmieren (CSV/Excel) | CSV, Excel, Google Sheets, Airtable, Notion (kostenlos) |
| Geplante Läufe | Cronjob + Hosting | Integrierter Scheduler |
| Umgang mit Rate Limits | Retries/Backoff selbst bauen | Wird automatisch behandelt |
| Am besten geeignet für | Entwickler, große Datenpipelines | Business-Anwender, schnelle Exporte, regelmäßiges Monitoring |
Nutzen Sie Python, wenn Sie volle Kontrolle brauchen oder das Ganze in eine größere Datenpipeline integrieren möchten. Nutzen Sie Thunderbit, wenn Sie Daten schnell brauchen und keinen Code pflegen möchten. Einen tieferen Einblick in Web Scraping für Preisvergleiche finden Sie in unserem separaten Leitfaden zu diesem Thema.

Tipps und Best Practices für das Scraping von Shopify-Stores
Diese gelten unabhängig vom Tool, das Sie verwenden:
- Immer
?limit=250verwenden, um die Anzahl der Requests zu minimieren. Der Standardwert von 30 pro Seite bedeutet 8x mehr Requests für dieselben Daten. - Den Store respektieren: Legen Sie zwischen den Requests 1–2 Sekunden Pause ein. Einen Server mit sehr vielen schnellen Anfragen zu belasten ist schlechte Praxis und erhöht die Blockierwahrscheinlichkeit.
- Zuerst
robots.txtprüfen: Shopifys Standard-robots.txtblockiert/products.jsonnicht. Manche Stores setzen aber eigene Regeln, daher vor großem Scraping kurz prüfen. - Rohes JSON zuerst lokal speichern, dann verarbeiten. Wenn sich Ihre Parsing-Logik später ändert, müssen Sie nicht noch einmal scrapen. Ein einfaches
json.dump(all_products, open("raw_data.json", "w"))vor dem Flattening spart viel Ärger. - Nach
product.iddeduplizieren: In Randfällen der Paginierung können an Seitenübergängen doppelte Produkte auftauchen. Ein schnellesdf.drop_duplicates(subset=["product_id", "variant_id"])behebt das. pricevor Berechnungen in Float umwandeln. Shopify liefert Preise als Strings ("185.00"), nicht als Zahlen.- Auf Änderungen am Endpunkt achten: Auch wenn
/products.jsonseit Jahren stabil ist, könnte Shopify ihn theoretisch einschränken. Wenn Ihr Scraper plötzlich 404 zurückgibt, prüfen Sie zuerst den Store manuell.
Für mehr zu robusten Scrapern lesen Sie unseren Leitfaden zu Best Practices für Web-Scraping-Tools.
Rechtliche und ethische Aspekte beim Scraping von Shopify
Kurz, aber wichtig.
Der /products.json-Endpunkt liefert öffentlich verfügbare Produktdaten – also genau die Informationen, die jeder Besucher beim Stöbern im Store sieht. In Shopifys Nutzungsbedingungen gibt es Formulierungen, die die Nutzung „automatisierter Mittel“ für den Zugriff auf „die Dienste“ untersagen, aber damit ist die Plattform selbst gemeint (Admin-Dashboard, Checkout), nicht die öffentlichen Storefront-Daten. Bis April 2026 wurden keine Shopify-spezifischen Scraping-Klagen eingereicht.
Wichtige Präzedenzfälle stützen das Scraping öffentlicher Daten: Der Fall hiQ v. LinkedIn stellte klar, dass das Scrapen öffentlich zugänglicher Daten keinen Verstoß gegen den CFAA darstellt, und Meta v. Bright Data (2024) entschied, dass TOS-Beschränkungen nur gelten, wenn ein Nutzer eingeloggt ist.
Best Practices:
- Nur öffentlich zugängliche Produktdaten scrapen
- Keine persönlichen oder Kundendaten scrapen
robots.txtund Rate Limits respektieren- DSGVO/CCPA einhalten, falls personenbezogene Daten verarbeitet werden (Produktkatalogdaten sind nicht personenbezogen)
- Sich mit einem klaren User-Agent-String identifizieren
- Den eigenen Shopify-Store über die Admin API zu scrapen ist immer unproblematisch
Für einen tieferen Einblick lesen Sie unseren Beitrag zu den rechtlichen Implikationen von Web Scraping.
Fazit und wichtigste Erkenntnisse
Shopifys öffentlicher /products.json-Endpunkt macht die Extraktion von E-Commerce-Daten so einfach wie kaum etwas anderes. Der Ablauf lautet: /products.json anhängen → mit Python abrufen → mit ?limit=250&page= paginieren → mit pandas flatten → als CSV oder Excel exportieren.
Was dieser Leitfaden im Vergleich zu anderen zusätzlich bietet:
- Vollständige Feldreferenz: Wissen Sie vor der ersten Codezeile genau, welche Daten verfügbar sind (40+ Felder über Produkte, Varianten und Bilder)
- Zusätzliche Endpunkte:
/collections.jsonund/meta.jsonliefern Kategorien-Insights und Store-Metadaten, die in anderen Tutorials fehlen - Produktionsreife Techniken: Session-Wiederverwendung, exponentielles Backoff, User-Agent-Header und
?limit=250für reale Rate-Limit-Situationen - Sauberer CSV-/Excel-Export: Mit pandas aufgelöste Variantendaten statt bloßer JSON-Dumps
- No-Code-Alternative: Thunderbit für alle, die Geschwindigkeit über Code-Flexibilität stellen
Für einmalige oder regelmäßige Shopify-Datenabzüge ohne Code probieren Sie die Thunderbit Chrome Extension Download Page – die Shopify Scraper-Vorlage übernimmt alles von der Paginierung bis zum Export. Für individuelle Datenpipelines oder Scraping im großen Stil über viele Stores hinweg bietet Ihnen das Python-Skript in diesem Leitfaden volle Kontrolle.
Schauen Sie sich unseren Thunderbit YouTube-Kanal für Video-Anleitungen an oder entdecken Sie unsere Leitfäden zu Amazon-Produkten scrapen und Daten von Websites nach Excel extrahieren für verwandte Techniken.
Thunderbit für Shopify-Scraping testen Get Started Free
FAQs
Kann man jeden Shopify-Store mit products.json scrapen?
Die meisten Shopify-Stores stellen diesen Endpunkt standardmäßig bereit – in Tests lieferten etwa 71 % gültiges JSON. Einige Stores mit individuellen Konfigurationen oder zusätzlichen Sicherheitsschichten (Cloudflare, Headless-Setups) geben möglicherweise 404 zurück oder blockieren die Anfrage. Der Schnellcheck: Öffnen Sie {store-url}/products.json im Browser. Wenn Sie JSON sehen, passt es.
Ist es legal, Shopify-Stores zu scrapen?
Öffentliche Produktdaten (Preise, Titel, Bilder, Beschreibungen) sind grundsätzlich zugänglich, und Präzedenzfälle wie hiQ v. LinkedIn stützen das Scraping öffentlich verfügbarer Informationen. Prüfen Sie dennoch immer die Nutzungsbedingungen des jeweiligen Stores und die Gesetze Ihres Landes. Scrapen Sie keine personenbezogenen oder Kundendaten und respektieren Sie Rate Limits.
Wie viele Produkte kann man aus einem Shopify-Store scrapen?
Es gibt keine feste Obergrenze für die Gesamtzahl. Mit der Paginierung über ?limit=250&page= rufen Sie den gesamten Katalog ab. Bei sehr großen Stores (25.000+ Produkte) sollten Sie Session-Wiederverwendung und Pausen nutzen, um Rate Limits zu vermeiden. Der /meta.json-Endpunkt zeigt Ihnen vorab die genaue Produktanzahl, sodass Sie wissen, wie viele Seiten zu erwarten sind.
Was ist der Unterschied zwischen products.json und der Shopify Admin API?
/products.json ist ein öffentlicher Endpunkt – keine Authentifizierung, nur lesbare Produktdaten, für jeden zugänglich. Die Admin API benötigt Zugriffstoken des Store-Besitzers und liefert Bestellungen, Bestandsmengen, Kundendaten und Schreibzugriff. Wenn Sie Verkaufsdaten oder echte Lagerbestände brauchen, benötigen Sie Admin-API-Zugriff, also die Berechtigung des Store-Besitzers.
Kann ich Shopify auch ohne Python scrapen?
Absolut. Tools wie Thunderbit Official Website ermöglichen es, Shopify-Stores direkt über eine Chrome-Erweiterung ohne Code zu scrapen. Die Paginierung wird automatisch verarbeitet und der Export erfolgt direkt nach CSV, Excel, Google Sheets, Airtable oder Notion. Für Entwickler, die andere Sprachen bevorzugen, funktioniert derselbe /products.json-Endpunkt auch mit JavaScript, Ruby, Go – also jeder Sprache, die HTTP-Requests senden und JSON parsen kann.
Mehr erfahren


