eBay-Scraping-Tutorials altern schnell – die meisten sind nach rund drei Monaten unbrauchbar. Das wissen wir bei Thunderbit aus eigener Anschauung, denn unser Team sieht immer wieder Entwickler, die über kaputte Code-Snippets, veraltete CSS-Selektoren und „funktionierende“ GitHub-Repos stolpern, die seit zwei eBay-Redesigns längst still und leise den Dienst quittiert haben.
eBay zählt rund 2,5 Milliarden aktive Angebote – nach Amazon der größte Long-Tail-Preisdatenbestand im offenen Web. Diese Daten treiben alles an, von Preisstrategien für Reseller bis zur Wettbewerbsanalyse. Nur ist der programmatische Zugriff ein bewegliches Ziel: eBays React-basierte Oberfläche tauscht CSS-Klassennamen am laufenden Band aus, A/B-Tests liefern je nach Nutzer unterschiedliche DOM-Strukturen, und der Akamai Bot Manager steht zwischen dir und dem HTML. Dieser Leitfaden liefert dir Python-Code, der heute funktioniert, erklärt, warum Scraper kaputtgehen, damit du robuste Lösungen baust, ordnet die Entscheidung zwischen eBay-API und Scraping ehrlich ein und zeigt eine No-Code-Alternative für Fälle, in denen Python den Aufwand nicht wert ist.
Was bedeutet es, eBay mit Python zu scrapen?
eBay mit Python zu scrapen heißt, Skripte zu schreiben, die eBay-Webseiten programmgesteuert abrufen, das HTML (oder verstecktes JSON) analysieren und strukturierte Daten extrahieren – etwa Titel, Preise, Verkäuferinfos, Verkaufsdaten oder Varianten – in ein Format, das du direkt weiterverwenden kannst, zum Beispiel CSV, Tabellenkalkulation oder Datenbank.
Verschiedene eBay-Seitentypen kommen dafür infrage:
- Suchergebnisse (z. B. alle „AirPods Pro“-Angebote)
- Einzelne Produktdetailseiten (vollständige Spezifikationen, Bilder, Verkäuferinfos)
- Verkaufte/abgeschlossene Angebote (tatsächliche Transaktionspreise und Daten)
- Verkäuferprofile und Bewertungen
Python ist dafür die Standardwahl. Das Ökosystem – Requests, BeautifulSoup, lxml, pandas – macht es einfach, Seiten abzurufen, HTML zu parsen und Daten zu verarbeiten. Wichtig ist allerdings der Unterschied zwischen dem Scrapen des Website-HTML und der Nutzung der offiziellen eBay-API – darauf komme ich gleich zurück.
Warum eBay scrapen? Praxisnahe Anwendungsfälle für Business-Teams
Wer das hier liest, hat wahrscheinlich schon einen konkreten Anlass. Trotzdem lohnt der Blick auf den geschäftlichen Nutzen, denn der ROI von eBay-Daten ist wirklich beeindruckend. Bain fand heraus, dass eine 1-%-Verbesserung des realisierten Preises zu einem Gewinnplus von 11,1 % führen kann – über tausende Unternehmen hinweg. McKinsey schreibt dynamischer Preisgestaltung im Handel bis zu 5 % Umsatzsteigerung und 2–7 % Margenverbesserung zu.
Die Anwendungsfälle, die mir am häufigsten begegnen:
| Anwendungsfall | Benötigte Daten | Geschäftlicher Nutzen |
|---|---|---|
| Preisbeobachtung & Repricing | Aktive Angebotspreise, Versand, Zustand | Wettbewerbsfähige Preisgestaltung, Marge schützen |
| Wettbewerbsanalyse | Produktsortimente, Aktionen, Versandkonditionen | Strategische Positionierung, Sortimentslücken erkennen |
| Marktforschung & Trendanalyse | Angebotsdynamik, Kategorietrends, Nachfrage-Muster | Neue Produkte identifizieren, Nachfrage prognostizieren |
| Reseller-Preisbewertung / Appraisals | Verkaufspreise, Verkaufsdaten, Zustand | Fairen Marktwert bestimmen, Buy-Box-Entscheidungen treffen |
| Sentiment-Analyse | Bewertungen, Sterne, Rückgaberichtlinien | Produktqualität und Kundenzufriedenheit besser verstehen |
| Lead-Generierung | Verkäuferprofile, Shop-Infos, Kontaktdaten | B2B-Outreach an Verkäufer mit hohem GMV |

Der gemeinsame Nenner: eBay hat die Daten, aber sie stecken in Webseiten.
Scraping ist der Weg, sie in einen Wettbewerbsvorteil zu verwandeln.
Offizielle eBay-API vs. Python-Webscraping: Was solltest du wählen?
Diese Frage stellen sich viele Tutorials zu selten ehrlich. eBay bietet offizielle APIs – allen voran die Browse API – und viele fragen sich, ob sie diese nutzen oder direkt scrapen sollen. Die Antwort hängt vollständig davon ab, welche Daten du brauchst.
| Kriterium | eBay Browse/Finding API | Python-Webscraping |
|---|---|---|
| Verkaufte/abgeschlossene Angebote | Eingeschränkt – Marketplace Insights API existiert, aber Zugriff wird oft abgelehnt | Vollzugriff über LH_Sold=1&LH_Complete=1 in der URL |
| Rate Limits | 5.000 Aufrufe/Tag im Basistarif | Selbst verwaltet (abhängig von Proxys) |
| Datenfelder | Vordefiniert (Titel, Preis, Kategorie, grundlegende Verkäuferdaten) | Alles, was auf der Seite sichtbar ist (Bewertungen, vollständige Spezifikationen, Variantenmatrix) |
| Einrichtungsaufwand | OAuth 2.0, App-Registrierung, API-Keys | pip install + Code |
| Stabilität | Stabile Endpunkte | Bricht, wenn sich das HTML ändert |
| Kosten | Freier Tarif verfügbar, bei höherem Volumen kostenpflichtig | Code kostenlos, Proxy-Kosten bei großem Umfang |
| Varianten-/MSKU-Daten | Teilweise – oft nur Parent-SKU | Vollständig (über verstecktes JSON) |
| Pagination-Tiefe | hartes Limit von 10.000 Einträgen | Theoretisch unbegrenzt |
Kurzer Hinweis: Die alte Finding API (mit findCompletedItems) wurde im Februar 2025 vollständig abgeschaltet. Verwendest du ebaysdk-python oder eine andere Bibliothek, die das Finding-Modul anspricht, funktioniert sie in der Produktion derzeit nicht mehr.
Meine Empfehlung: Nimm die Browse API für stabile, strukturierte Katalogabfragen mit moderatem Volumen bei aktiven Angeboten. Setze auf Python-Scraping, wenn du Verkaufspreise, Bewertungen, Variantendaten oder andere Felder brauchst, die die API nicht liefert. Viele Teams nutzen beides.
Welche Tools und Bibliotheken du brauchst, um eBay mit Python zu scrapen
Bevor wir Code schreiben, hier das Werkzeug-Set. Für die meisten eBay-Seiten brauchst du keinen Headless Browser – die Daten stecken im serverseitig gerenderten HTML.
| Bibliothek | Zweck |
|---|---|
requests oder httpx | HTTP-Client zum Herunterladen von eBay-Seiten |
curl_cffi | HTTP-Client mit echtem Browser-TLS-Fingerprint (entscheidend gegen Akamai) |
beautifulsoup4 | HTML-Parser für das Extrahieren per CSS-Selektor |
lxml | Schnelles Parser-Backend für BeautifulSoup |
jmespath | Abfragesprache für verschachtelte JSON-Blöcke |
pandas | Datenbearbeitung sowie CSV-/Excel-Export |
gspread | Integration mit Google Sheets |
Installation in einem Schritt:
pip install requests httpx curl_cffi beautifulsoup4 lxml jmespath pandas gspread
Nutze Python 3.11+ – pandas 3.0 benötigt 3.10+, und 3.11 bringt bei I/O-lastigen Aufgaben 10–60 % mehr Geschwindigkeit.
Eine Bibliothek sticht heraus: curl_cffi ist 2026 das wichtigste Upgrade, das ein eBay-Scraper machen kann. eBay setzt den Akamai Bot Manager ein, und Akamais wichtigste Erkennungsmethode ist das TLS-Fingerprinting. Normales requests erzeugt einen Python-typischen JA3-Fingerprint und wird sofort markiert. curl_cffi imitiert den TLS-Handshake eines echten Chrome-Browsers und kommt damit bei rund 90 % der Akamai-geschützten Ziele ohne Headless Browser aus.
Thunderbit für jede Website testen
Schritt für Schritt: eBay-Suchergebnisse mit Python scrapen
Das ist das Kern-Tutorial. Wir scrapen eBay-Suchergebnisseiten für Produktangebote.
- Schwierigkeitsgrad: Anfänger bis Mittelstufe
- Benötigte Zeit: ca. 30 Minuten bis zum ersten funktionierenden Scrape
- Was du brauchst: Python 3.11+, die oben genannten Bibliotheken, ein Terminal und eine Ziel-URL für die eBay-Suche
Schritt 1: Python-Projekt einrichten
Lege ein Projektverzeichnis an und installiere die Abhängigkeiten:
mkdir ebay-scraper && cd ebay-scraper
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install requests curl_cffi beautifulsoup4 lxml pandas
Erstelle eine Datei namens scrape_ebay.py. Das ist dein Arbeitsbereich.
Schritt 2: Die eBay-Such-URL bauen
eBays Such-URL-Struktur ist recht überschaubar. Der wichtigste Parameter ist _nkw (Keyword):
import urllib.parse
keyword = "airpods pro"
base_url = "https://www.ebay.com/sch/i.html"
params = {
"_nkw": keyword,
"_ipg": "120", # Einträge pro Seite: 60, 120 oder 240 (240 kann Bot-Flags auslösen)
"_pgn": "1", # Seitenzahl
}
url = f"{base_url}?{urllib.parse.urlencode(params)}"
print(url)
# https://www.ebay.com/sch/i.html?_nkw=airpods+pro&_ipg=120&_pgn=1
Weitere nützliche Parameter:
LH_BIN=1— nur „Sofort-Kaufen“_sacat=175673— bestimmte Kategorie_sop=12— Sortierung nach bester Übereinstimmung (10 = niedrigster Preis inkl. Versand, 13 = neu eingestellt)LH_Complete=1&LH_Sold=1— verkaufte/abgeschlossene Angebote (weiter unten in einem eigenen Abschnitt erklärt)
Schritt 3: Anfrage senden und Antwort verarbeiten
Jetzt zeigt curl_cffi, was es kann. Ein einfaches requests.get() liefert oft einen 403 von Akamai. Mit curl_cffi imitieren wir einen echten Chrome-Browser:
from curl_cffi import requests as cffi_requests
import random, time
USER_AGENTS = [
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36",
"Mozilla/5.0 (X11; Linux x86_64; rv:124.0) Gecko/20100101 Firefox/124.0",
]
HEADERS = {
"User-Agent": random.choice(USER_AGENTS),
"Accept-Language": "en-US,en;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
}
def fetch_page(url, max_retries=5):
delay = 2
for attempt in range(max_retries):
try:
r = cffi_requests.get(url, impersonate="chrome124", headers=HEADERS, timeout=30)
if r.status_code == 200:
return r.text
if r.status_code in (403, 429, 503):
retry_after = r.headers.get("Retry-After")
sleep_for = float(retry_after) if retry_after else delay + random.uniform(0, 1)
print(f" Status {r.status_code}, erneuter Versuch in {sleep_for:.1f}s...")
time.sleep(sleep_for)
delay *= 2
continue
r.raise_for_status()
except Exception as e:
print(f" Request-Fehler: {e}, erneuter Versuch...")
time.sleep(delay)
delay *= 2
raise RuntimeError(f"Nach {max_retries} Versuchen fehlgeschlagen: {url}")
Exponentielles Backoff mit Jitter ist entscheidend – feste Warteintervalle sind selbst wieder ein Bot-Fingerabdruck.
Schritt 4: Produktangebote aus der Suchseite parsen
eBay steckt gerade mitten in der Migration zwischen zwei Suchergebnis-Layouts. Ein robuster Scraper muss beide beherrschen:
| Feld | Altes Layout | Neues Layout |
|---|---|---|
| Karten-Container | li.s-item | li.s-card oder div.su-card-container |
| Titel | .s-item__title | .s-card__title |
| URL | a.s-item__link[href] | a.su-link[href] |
| Preis | span.s-item__price | .s-card__price |
Der Parsing-Code, der beide Layouts verarbeitet:
from bs4 import BeautifulSoup
def parse_search_results(html):
soup = BeautifulSoup(html, "lxml")
cards = soup.select("li.s-item, li.s-card, div.su-card-container")
results = []
for card in cards:
# Titel — beide Layouts prüfen
title_el = card.select_one(".s-item__title, .s-card__title")
title = title_el.get_text(strip=True) if title_el else None
# Phantom-Karte „Shop on eBay“ überspringen
if not title or "Shop on eBay" in title:
continue
# Preis
price_el = card.select_one("span.s-item__price, .s-card__price")
price = price_el.get_text(strip=True) if price_el else None
# URL
link_el = card.select_one("a.s-item__link[href], a.su-link[href]")
url = link_el["href"].split("?")[0] if link_el else None
# Bild
img_el = card.select_one("img.s-item__image-img, .s-card__image img")
image = None
if img_el:
image = img_el.get("src") or img_el.get("data-src")
# Versand
ship_el = card.select_one("span.s-item__shipping, span.s-item__logisticsCost, .s-card__attribute-row")
shipping = ship_el.get_text(strip=True) if ship_el else None
results.append({
"title": title,
"price": price,
"url": url,
"image": image,
"shipping": shipping,
})
return results
Die erste Phantom-Karte ist ein klassischer Stolperstein. Das erste li.s-item auf vielen eBay-Suchergebnisseiten ist ein versteckter Platzhalter mit dem Titel „Shop on eBay“ und ohne echten Preis. Immer herausfiltern.
Schritt 5: Pagination für mehrere Seiten behandeln
eBay paginiert über den _pgn-Parameter. Der Link zur nächsten Seite verwendet a.pagination__next:
import urllib.parse
def scrape_ebay_search(keyword, max_pages=5):
all_results = []
for page_num in range(1, max_pages + 1):
params = {"_nkw": keyword, "_ipg": "120", "_pgn": str(page_num)}
url = f"https://www.ebay.com/sch/i.html?{urllib.parse.urlencode(params)}"
print(f"Scrape Seite {page_num}: {url}")
html = fetch_page(url)
results = parse_search_results(html)
if not results:
print(f" Keine Ergebnisse auf Seite {page_num}, Abbruch.")
break
all_results.extend(results)
print(f" {len(results)} Angebote gefunden (insgesamt: {len(all_results)})")
# Höfliche Pause — 3 bis 8 Sekunden mit Jitter
time.sleep(random.uniform(3, 8))
return all_results
Der zufällige Jitter von 3–8 Sekunden ist nicht optional.
eBays Akamai-Schicht markiert dauerhaft mehr als 1 Anfrage/Sekunde von einer einzelnen IP.
Schritt 6: Scraped Daten als CSV oder JSON exportieren
import pandas as pd
results = scrape_ebay_search("airpods pro", max_pages=3)
df = pd.DataFrame(results)
df.to_csv("ebay_airpods.csv", index=False)
df.to_json("ebay_airpods.json", orient="records", indent=2)
print(f"{len(df)} Angebote als CSV und JSON exportiert.")
Jetzt solltest du eine saubere Tabelle mit eBay-Angeboten haben. Auf meinem Rechner dauerte das Scrapen von 3 Seiten (360 Angebote) inklusive Pausen etwa 45 Sekunden.
Wie man eBay-Produktdetailseiten mit Python scrapt
Suchergebnisse liefern nur eine Zusammenfassung. Die spannenden Daten stecken auf den Produktdetailseiten: vollständige Beschreibungen, Verkäufer-Bewertungen, Artikelspezifikationen, Bildergalerien und Varianten.
Eine einzelne Produktseite parsen
eBay-Artikelseiten liegen unter /itm/<ITEM_ID>. Der stabilste Extraktionsweg ist JSON-LD – eBay bettet einen Product-Schema-Block ein, der fast alle CSS-Änderungen übersteht:
import json
def parse_item_page(html):
soup = BeautifulSoup(html, "lxml")
item = {}
# 1. JSON-LD — stabilster Extraktionsweg
for tag in soup.find_all("script", type="application/ld+json"):
try:
data = json.loads(tag.string or "")
except (json.JSONDecodeError, TypeError):
continue
if isinstance(data, dict) and data.get("@type") == "Product":
item["title"] = data.get("name")
item["brand"] = (data.get("brand") or {}).get("name")
item["images"] = data.get("image")
offers = data.get("offers") or {}
item["price"] = offers.get("price")
item["currency"] = offers.get("priceCurrency")
break
# 2. CSS-Fallbacks für Felder, die nicht in JSON-LD stehen
def first_text(selectors):
for sel in selectors:
el = soup.select_one(sel)
if el and el.get_text(strip=True):
return el.get_text(strip=True)
return None
item.setdefault("title", first_text([
"h1.x-item-title__mainTitle",
"h1.x-item-title__mainTitle .ux-textspans--BOLD",
]))
item["condition"] = first_text([
".x-item-condition-text .ux-textspans",
])
item["seller"] = first_text([
".x-sellercard-atf__info__about-seller a .ux-textspans",
])
item["shipping"] = first_text([
"div.ux-labels-values--shipping .ux-textspans--BOLD",
])
# 3. Artikelspezifikationen
specifics = {}
for dl in soup.select(".ux-layout-section-evo__item--table-view dl.ux-labels-values"):
k = dl.select_one(".ux-labels-values__labels-content .ux-textspans")
v = dl.select_one(".ux-labels-values__values-content .ux-textspans")
if k and v:
specifics[k.get_text(strip=True).rstrip(":")] = v.get_text(strip=True)
item["specifics"] = specifics
return item
Das Muster hier – erst JSON-LD, dann CSS-Fallbacks – ist der Schlüssel zu Scrapern, die nicht jedes Quartal zerbrechen. Dazu gleich mehr.
eBay-Produktvarianten scrapen (MSKU-Daten)
Manche eBay-Angebote haben mehrere Varianten – verschiedene Farben, Größen oder Speicherkapazitäten. Das sichtbare DOM zeigt zunächst nur eine Preisspanne wie „899 bis 1.099 €“, bis der Nutzer eine Option anklickt. Die tatsächlichen Preise pro Variante liegen in einem versteckten JavaScript-Objekt namens MSKU.
Genau hier liefert die eBay-API nur teilweise Daten (Parent-SKU), weshalb Scraping die bessere Wahl ist.
import re, json
def extract_variants(html):
# Non-greedy ist entscheidend — ein gieriges .+ verschluckt die gesamte Seite
m = re.search(r'"MSKU"\s*:\s*(\{.+?\})\s*,\s*"QUANTITY"', html, re.DOTALL)
if not m:
return []
try:
msku = json.loads(m.group(1))
except json.JSONDecodeError:
return []
item_labels = {str(k): v["displayLabel"] for k, v in msku.get("menuItemMap", {}).items()}
skus = []
for combo_key, variation_id in msku.get("variationCombinations", {}).items():
option_ids = combo_key.split("_")
options = [item_labels.get(oid, oid) for oid in option_ids]
var = msku.get("variationsMap", {}).get(str(variation_id), {})
bin_model = var.get("binModel", {})
price_spans = bin_model.get("price", {}).get("textSpans", [{}])
price = price_spans[0].get("text") if price_spans else None
qty = var.get("quantity")
skus.append({
"options": options,
"price": price,
"quantity_available": qty,
"variation_id": variation_id,
})
return skus
Genau dieses nicht-gierige (.+?) bringt fast jeden eBay-Scraper zu Fall. Ein gieriges .+ frisst alles bis zum letzten "QUANTITY" auf der Seite und erzeugt dadurch fehlerhaftes JSON. Diesen Bug habe ich in mindestens drei „funktionierenden“ Tutorials gesehen.
Wie man verkaufte und abgeschlossene eBay-Angebote mit Python scrapt
Das ist der Anwendungsfall, der Scraping gegenüber der API rechtfertigt. Verkaufsdaten – was tatsächlich verkauft wurde, zu welchem Preis und an welchem Datum – sind der Goldstandard für Marktforschung, Reseller-Preise und Bewertungen. Die eBay Browse API liefert das ausdrücklich nicht. Die Marketplace Insights API tut das technisch zwar, aber der Zugriff ist ein „Limited Release“ und wird häufig abgelehnt.
Die URL-Parameter, die du brauchst, sind LH_Complete=1 (abgeschlossene Angebote) und LH_Sold=1 (nur tatsächlich verkaufte). Du musst beide setzen. LH_Sold=1 allein fällt in manchen Kategorien stillschweigend auf aktive Angebote zurück – die häufigste Falle in der Community.
def scrape_sold_listings(keyword, max_pages=3):
all_sold = []
for page_num in range(1, max_pages + 1):
params = {
"_nkw": keyword,
"_ipg": "120",
"_pgn": str(page_num),
"LH_Complete": "1",
"LH_Sold": "1",
}
url = f"https://www.ebay.com/sch/i.html?{urllib.parse.urlencode(params)}"
print(f"Verkaufte Seite {page_num scrapen...")
html = fetch_page(url)
soup = BeautifulSoup(html, "lxml")
cards = soup.select("li.s-item")
for card in cards:
title_el = card.select_one(".s-item__title")
title = title_el.get_text(strip=True) if title_el else None
if not title or "Shop on eBay" in title:
continue
# Nur tatsächlich verkaufte Artikel einbeziehen (grünes POSITIVE-Preislabel)
sold_tag = card.select_one(
".s-item__title--tag .POSITIVE, .s-item__caption--signal.POSITIVE"
)
if sold_tag is None:
continue # Nicht verkauftes, aber abgeschlossenes Angebot — überspringen
price_el = card.select_one("span.s-item__price")
price = price_el.get_text(strip=True) if price_el else None
# Verkaufsdatum parsen
sold_date = None
import re, datetime as dt
card_text = card.get_text()
m = re.search(r"Sold\s+([A-Z][a-z]{2}\s+\d{1,2},\s*\d{4})", card_text)
if m:
sold_date = dt.datetime.strptime(m.group(1), "%b %d, %Y").strftime("%Y-%m-%d")
link_el = card.select_one("a.s-item__link[href]")
url = link_el["href"].split("?")[0] if link_el else None
all_sold.append({
"title": title,
"sold_price": price,
"sold_date": sold_date,
"url": url,
})
if not cards:
break
time.sleep(random.uniform(3, 8))
return all_sold
Der entscheidende HTML-Unterschied: Verkaufte Artikel zeigen den Preis grün (innerhalb eines .POSITIVE-Wrappers), während nicht verkaufte, aber abgelaufene Angebote den Preis rot und durchgestrichen anzeigen. Deshalb immer auf die .POSITIVE-Klasse filtern.
Warum eBay-Scraper kaputtgehen – und wie du robuste baust
Wenn dein eBay-Scraper nicht mehr läuft, bist du in guter Gesellschaft. Das ist das Problem Nr. 1 in jedem eBay-Scraping-Forum, das ich gelesen habe. Die Frage ist nicht ob dein Scraper kaputtgeht, sondern wann.
Warum das passiert:
- eBay nutzt React-basiertes Rendering mit dynamisch erzeugten Klassennamen, die sich bei Deployments ändern
- A/B-Tests liefern je nach Nutzer unterschiedliche DOM-Strukturen (die dualen
s-item/s-card-Layouts sind gerade ein lebendes Beispiel) - Regelmäßige Redesigns ändern die HTML-Verschachtelung, selbst wenn die Daten gleich bleiben
- Alte Selektoren wie
#itemTitleund#prcIsumwurden vor Jahren entfernt, tauchen aber in Tutorials immer noch auf
Wie Scrapflys Leitfaden für 2026 sagt: „Die eigentliche Herausforderung beim eBay-Webscraping besteht darin, mit den CSS-Selektor-Änderungen von eBay umzugehen. eBay aktualisiert das Frontend regelmäßig und bricht damit Scraper, die auf bestimmten Klassennamen beruhen.“

Strategien für langlebige eBay-Scraper
Vier Strategien, die eBays vierteljährliche Umbauten überstehen:
1. JSON-LD vor CSS-Selektoren priorisieren. eBay bettet auf jeder Artikelseite strukturierte Product-Schema-Daten ein. Die Datenebene ändert sich viel seltener als die Darstellungsebene – Designer überarbeiten CSS-Klassen jedes Quartal, aber Backend-Feldnamen wie price, name und seller hängen an internen APIs und werden selten umbenannt.
2. Kaskadierende Fallback-Selektoren nutzen. Verlass dich nie auf einen einzigen CSS-Selektor. Immer Alternativen angeben:
def first_text(soup, selectors):
for sel in selectors:
el = soup.select_one(sel)
if el and el.get_text(strip=True):
return el.get_text(strip=True)
return None
title = first_text(soup, [
"h1.x-item-title__mainTitle",
"h1.x-item-title__mainTitle .ux-textspans--BOLD",
"[data-testid='x-item-title'] h1",
])
3. Versteckte JSON-Blöcke parsen. Das MSKU-Variantenobjekt und Inline-JavaScript-Daten überstehen CSS-Änderungen, weil sie serverseitig erzeugt werden. Regex-Extraktion aus <script>-Tags ist anfangs mehr Arbeit, senkt aber den Wartungsaufwand deutlich.
4. Fehler bei Selektoren protokollieren. Baue Monitoring ein, damit du weißt, wann ein Selektor nicht mehr greift – nicht erst, dass deine Daten leer sind:
if title is None:
print(f"WARNUNG: Titel-Selektor fehlgeschlagen für {url}")
5. curl_cffi mit Browser-Impersonation verwenden. Damit umgehst du Akamais TLS-Fingerprinting ohne Headless Browser.
Die KI-Alternative: Kein Selektor-Pflegeaufwand
Wer es leid ist, alle paar Monate Selektoren nachzubessern, findet hier einen grundlegend anderen Ansatz. Tools wie Thunderbit nutzen KI, um die Seite bei jedem Lauf neu zu lesen und die Extraktionslogik dynamisch abzuleiten. Eine Studie der McGill University verglich KI- und Selektor-basierte Scraper auf 3.000 Seiten und stellte fest, dass KI-Methoden selbst nach Layout-Änderungen eine Genauigkeit von 98,4 % hielten, während Branchen-Benchmarks 60–80 % weniger Wartungsaufwand angeben.
| Ansatz | Bricht bei HTML-Änderungen von eBay? | Wartungsaufwand |
|---|---|---|
| Fest codierte CSS-Selektoren | Ja, vierteljährlich | Hoch — laufende Anpassungen |
| Extraktion aus verstecktem JSON / JSON-LD | Selten | Niedrig |
| KI-basiertes Scraping (Thunderbit) | Nein — KI leitet Selektoren bei jedem Lauf neu ab | Keiner |
eBay-Daten mit KI scrapen Get Started Free
Den Thunderbit-Workflow zeige ich später im Detail. Wichtig ist vorerst: Willst du einen Scraper bauen, der über Monate laufen soll, setze auf JSON-first-Extraktion und Fallback-Selektoren. Willst du Selektoren gar nicht pflegen, lohnt sich ein Blick auf den KI-Ansatz.
Wiederkehrende eBay-Scrapes für Preisbeobachtung automatisieren
Ein einmaliges Scraping ist nützlich. Preisbeobachtung, Lagerbestandstracking und Wettbewerbsanalyse brauchen jedoch regelmäßige Datenerfassung. Jeder Konkurrenzartikel, den ich gelesen habe, nennt Preismonitoring als Anwendungsfall – aber fast keiner zeigt, wie man es tatsächlich automatisiert.
Option 1: Cron-Jobs (Linux/macOS) oder Task Scheduler (Windows)
Der einfachste Weg. Verpacke dein Python-Skript in einen Cron-Job. Verwende immer den absoluten Pfad zu Python in deiner venv – Cron startet mit einer minimalen Umgebung:
crontab -e
# Täglich um 08:15
15 8 * * * /Users/me/ebay/venv/bin/python /Users/me/ebay/scrape_ebay.py >> /Users/me/ebay/scrape.log 2>&1
Unter Windows nutzt du PowerShell:
$A = New-ScheduledTaskAction -Execute "C:\Users\me\ebay\venv\Scripts\python.exe" -Argument "C:\Users\me\ebay\scrape_ebay.py"
$T = New-ScheduledTaskTrigger -Daily -At 8:15am
Register-ScheduledTask -TaskName "eBayScraper" -Action $A -Trigger $T
Das setzt einen dauerhaft laufenden Rechner voraus, und Proxys sowie Anti-Bot-Maßnahmen verwaltest du selbst.
Option 2: Cloud Functions (Serverless)
Mit AWS Lambda oder Google Cloud Functions betreibst du Scraper ohne eigenen Server. Der Einrichtungsaufwand ist höher – du musst Abhängigkeiten paketieren, Timeouts berücksichtigen (Lambda ist auf 15 Minuten begrenzt) und trotzdem Proxys managen. Dafür entfällt die Serverwartung.
Option 3: No-Code-Zeitplanung mit Thunderbit
Mit der Scheduled-Scraper-Funktion von Thunderbit beschreibst du das Intervall in Klartext (z. B. „jeden Tag um 8 Uhr“), fügst eBay-URLs ein und klickst auf Planen. Der Lauf erfolgt in der Cloud mit integriertem Anti-Bot-Schutz.
| Ansatz | Einrichtungsaufwand | Eigener Server nötig? | Anti-Bot-Schutz enthalten? |
|---|---|---|---|
| Cron + Python-Skript | Mittel | Ja (immer laufender Rechner) | Proxys musst du selbst managen |
| Cloud Function (Lambda) | Hoch | Nein (serverless) | Proxys musst du selbst managen |
| Thunderbit Scheduled Scraper | Niedrig (per Beschreibung) | Nein (cloudbasiert) | Integriert |
Zum Speichern regelmäßiger Scrape-Daten ist eine lokale SQLite-Datenbank die richtige Wahl für Preisverläufe. Verwende ON CONFLICT ... DO UPDATE (nicht INSERT OR REPLACE, das Foreign Keys kaputtmacht und Spalten löscht):
CREATE TABLE IF NOT EXISTS listings (
item_id TEXT PRIMARY KEY,
title TEXT NOT NULL,
price REAL,
last_price REAL,
first_seen_at TEXT DEFAULT (datetime('now')),
last_seen_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS price_history (
item_id TEXT NOT NULL,
observed_at TEXT NOT NULL DEFAULT (datetime('now')),
price REAL NOT NULL,
PRIMARY KEY (item_id, observed_at)
);
Thunderbit Scheduled Scraper testen
Keine Lust auf Code? So scrapst du eBay in 2 Minuten mit Thunderbit
Ich habe jetzt 2.000 Wörter über Python-Code geschrieben. Jetzt will ich ehrlich sein, wann du das gar nicht brauchst.
Machst du als Business-Nutzer einmalig Marktforschung, prüfst du als Reseller Vergleichsdaten oder brauchst du als E-Commerce-Team Daten heute, ohne einen Dev-Sprint zu starten, ist Python oft überdimensioniert. Die Einrichtung, die Pflege der Selektoren, das Proxy-Management – das ist viel Aufwand für „Ich brauche nur diese 200 Angebote in einer Tabelle“.
So scrapt Thunderbit eBay (Schritt für Schritt)
- Installiere die Thunderbit Chrome-Erweiterung – keine Kreditkarte erforderlich.
- Öffne in Chrome eine beliebige eBay-Suchergebnisseite oder Produktseite.
- Klicke in der Thunderbit-Seitenleiste auf „AI Suggest Fields“. Die KI liest die Seite und schlägt Spalten vor: Titel, Preis, Zustand, Versand, Verkäufer, Bewertung.
- Klicke auf „Scrape“. Die Erweiterung folgt der Pagination und füllt die Datentabelle. Für eBay gibt es bei Thunderbit vorgefertigte Instant-Scraper-Templates, die per Klick funktionieren.
- Exportiere kostenlos nach Google Sheets, Airtable, Notion, CSV, JSON oder Excel.
Der gesamte Ablauf dauert unter 2 Minuten.
Ich habe es gestoppt.
Unterseiten-Anreicherung: Detailseiten-Daten ohne zusätzlichen Code
Nach dem Scrapen einer Suchergebnisseite kann Thunderbit jede einzelne Angebots-Detailseite besuchen und zusätzliche Felder anhängen – vollständige Spezifikationen, Verkäuferinfos, Beschreibung, alle Bilder. Damit ersetzt du die 20+ Zeilen Python-Code für das Scrapen von Unterseiten, die wir oben geschrieben haben, durch einen einzigen Klick.
Wann Python trotzdem die bessere Wahl ist
Python gewinnt, wenn du Folgendes brauchst:
- Scraping in großem Umfang (Zehntausende Seiten pro Lauf)
- Tief angepasste Parsing-Logik oder Datenumwandlung
- Einbindung in bestehende Datenpipelines (Airflow, dbt, Kafka)
- Feingranulare TLS-/Session-Steuerung für fortgeschrittene Anti-Bot-Arbeit
- Unit Economics – bei Millionen von Zeilen ist ein gepflegter Stack günstiger als ein kreditbasiertes SaaS-Modell
Für die meisten einmaligen oder mittelgroßen Projekte ist Thunderbit schneller und einfacher. Für Produktionspipelines im großen Maßstab gibt dir Python maximale Kontrolle.
Tipps, um beim Scrapen von eBay mit Python nicht blockiert zu werden
eBays Akamai-Schicht ist real. Was in der Praxis wirklich funktioniert:
- Nutze
curl_cffimitimpersonate="chrome124"— das ist die mit Abstand größte Verbesserung gegenüber einfachemrequests - Wechsle User-Agent-Strings aus einer Liste aktueller Browser-Versionen (Chrome 143, Firefox 124, Safari 26)
- Baue zufällige Pausen von 3–8 Sekunden zwischen Anfragen ein — feste Intervalle sind ein Fingerabdruck
- Verwende Residential- oder rotierende Proxys für alles über ein paar Dutzend Seiten. Rechenzentrums-IP-Adressen (AWS, GCP, DigitalOcean) erkennt Akamai schnell.
- Beachte die
robots.txt— viele gefilterte Browse-URLs sind ausdrücklich verboten; Artikeldetailseiten (/itm/<id>) dagegen nicht - Behandle CAPTCHAs sauber — erkenne sie und versuche es mit einer anderen IP erneut oder nutze einen CAPTCHA-Lösungsdienst
- Belaste den Server nicht unnötig. Der Präzedenzfall eBay v. Bidder’s Edge zeigt, dass „trespass to chattels“ relevant wird, wenn Scraping Server tatsächlich beeinträchtigt. Mit 1 Anfrage/Sekunde pro IP bleibst du weit darunter.
Für kommerzielle Nutzung mit hohem Volumen ist es oft am saubersten, die Browse API für aktive Angebote zu verwenden und gezielt nur für Verkaufspreise und Felder zu scrapen, die die API nicht liefert. Dieser Hybridansatz ist technisch und rechtlich meist die bessere Lösung.
Ist es legal, eBay mit Python zu scrapen?
Ich bin kein Anwalt, und dieser Beitrag ist keine Rechtsberatung. Deshalb halte ich mich kurz.
Die Rechtslage hat sich zugunsten des Scrapings öffentlich zugänglicher Daten verschoben. Wichtige Präzedenzfälle:
- hiQ v. LinkedIn (9. Cir., 2022): Scraping öffentlich zugänglicher Daten verstößt nicht gegen den CFAA
- Van Buren v. United States (US Supreme Court, 2021): schränkte die CFAA-Klausel „exceeds authorized access“ ein
- Meta v. Bright Data (N.D. Cal., 2024): Scraping im ausgeloggten Zustand verletzt nicht die Plattform-AGB, weil der Scraper kein „Nutzer“ ist
Allerdings verbietet eBays Nutzervereinbarung-Update vom Februar 2026 ausdrücklich „Buy-for-me-Agenten, LLM-gesteuerte Bots oder End-to-End-Abläufe, die versuchen, Bestellungen ohne menschliche Prüfung aufzugeben“. Die Grenze ist klar: schreibgeschütztes Scraping öffentlicher Seiten ist rechtlich deutlich solider; Checkout-Automatisierung ist es nicht.
Best Practices: Scrape nur öffentlich sichtbare Daten. Erstelle keine Fake-Accounts und umgehe keine Login-Schranken. Verbreite keine urheberrechtlich geschützten Angebotsbilder in großem Stil weiter. Und ziehe bei kommerziellen Großprojekten Rechtsberatung hinzu.
Fazit und wichtigste Erkenntnisse
Python ist der flexibelste Weg, eBay zu scrapen – verlangt aber laufende Pflege, sobald sich das HTML der Website ändert. Der Entscheidungsrahmen:
- Nutze die eBay Browse API für stabile, strukturierte Abfragen mit moderatem Volumen bei aktiven Angeboten
- Nutze Python-Scraping für verkaufte Angebote, Bewertungen, Variantendaten und alles, was die API nicht preisgibt
- Nutze Thunderbit, wenn du eBay-Daten ohne Code oder Wartungsaufwand möchtest
Der Code in diesem Leitfaden setzt auf Robustheit: erst JSON-LD-Extraktion, dann kaskadierende CSS-Fallbacks, bei Varianten zusätzlich verstecktes JSON. Dieser mehrschichtige Ansatz sorgt dafür, dass dein Scraper nicht beim nächsten Frontend-Redesign von eBay ausfällt.
Willst du den No-Code-Weg testen, legst du mit dem kostenlosen Tarif von Thunderbit sofort auf eBay-Seiten los. Und wenn du sehen willst, wie die eBay-Scraper-Vorlage funktioniert, ist sie nur einen Klick entfernt.
Mehr zu Web-Scraping-Tools findest du in unseren Leitfäden zu den besten automatisierten Web-Scraping-Tools, Daten von Websites nach Excel scrapen und den besten Python-Web-Scraping-Tools. Außerdem kannst du Tutorials auf dem Thunderbit YouTube-Kanal ansehen.
Thunderbit für eBay-Scraping testen Get Started Free
FAQs
1. Kann ich eBay mit Python kostenlos scrapen?
Ja. Alle Bibliotheken (Requests, BeautifulSoup, curl_cffi, pandas) sind kostenlos und Open Source. Kosten entstehen erst im größeren Maßstab – Residential Proxys für hohes Volumen liegen je nach Bandbreite meist bei 50–500 $ pro Monat. Für kleine Projekte (ein paar hundert Seiten) kannst du mit sauberem Rate Limiting auch von deiner Heim-IP scrapen.
2. Wie scrape ich verkaufte eBay-Artikel und abgeschlossene Angebote mit Python?
Füge LH_Complete=1&LH_Sold=1 zu deinen Such-URL-Parametern hinzu. Du musst beide setzen – LH_Sold=1 allein fällt in manchen Kategorien stillschweigend auf aktive Angebote zurück. Filtere die Ergebnisse, indem du beim Preiselement auf die CSS-Klasse .POSITIVE prüfst; sie zeigt einen tatsächlichen Verkauf an und nicht ein nicht verkauftes, abgelaufenes Angebot.
3. Blockiert eBay Webscraping?
eBay nutzt den Akamai Bot Manager, der Scraper vor allem über TLS-Fingerprinting und Verhaltensanalyse erkennt. Einfache requests-Aufrufe liefern oft 403-Antworten. Mit curl_cffi, Browser-Imitation, rotierenden User-Agents und zufälligen Pausen von 3–8 Sekunden zwischen den Anfragen lassen sich die meisten Blockaden umgehen. Residential Proxys helfen im größeren Maßstab.
4. Sollte ich die eBay-API oder Webscraping nutzen?
Nutze die Browse API für stabile Abfragen mit moderatem Volumen bei aktiven Angeboten (bis zu 5.000 Aufrufe/Tag). Scraping ist sinnvoll, wenn du Preisverläufe verkaufter Artikel, vollständige Varianten-/MSKU-Daten, Bewertungen oder andere Felder brauchst, die die API nicht anbietet. Die Marketplace Insights API liefert technisch verkaufte Daten, der Zugriff ist jedoch eingeschränkt und wird häufig abgelehnt.
5. Was ist der einfachste Weg, eBay ohne Programmierung zu scrapen?
Die Thunderbit Chrome-Erweiterung nutzt KI, um eBay-Seiten zu lesen, passende Datenfelder vorzuschlagen und Angebote mit einem Klick zu extrahieren. Sie übernimmt Pagination, Unterseiten-Anreicherung und Export nach Google Sheets, Excel, Airtable oder Notion. Vorgefertigte eBay-Scraper-Vorlagen machen häufige Anwendungsfälle noch schneller.
Mehr erfahren
- Python-Webscraper meistern: Schritt-für-Schritt-Beispiel
- Python-Web-Scraping-Tutorial: Daten von einer Website ziehen
- So nutzt du BeautifulSoup: Ein Python-Web-Scraping-Tutorial
- Python-Web-Scraping-Tutorial: Wie man eine Website scrapt
- Wie man mit Python einen Webscraper schreibt: Von Anfang bis Ende


