API vs. HTML-Scraping: Wann du eBay direkt scrapen solltest
Wenn du eBay-Daten brauchst, stehen dir zwei Wege offen: die offiziellen eBay Finding- und Browse-APIs oder klassisches HTML-Scraping. Die APIs sind sauber und stabil – aber sie haben harte Limits, die für ernsthafte Marktforschung oder Reseller-Intelligence schnell zum Flaschenhals werden.
Die Finding API erlaubt typischerweise 5.000 Calls pro Tag pro App-Key. Die Browse API (Teil der Buy API-Familie) ist für Käufer-Szenarien gedacht und verlangt oft OAuth-Tokens plus ein aktives eBay-Entwicklerprogramm. Für sporadische Abfragen reicht das – für breite Preisüberwachung, Konkurrenz-Analyse oder Auktions-Tracking über Tausende von Listings pro Stunde jedoch nicht.
Deswegen greifen Teams auf HTML-Scraping zurück: keine API-Quoten, kein OAuth-Overhead, Zugriff auf alles, was im Browser sichtbar ist – inklusive Auktions-Countdowns, Verkäufer-Feedback-Scores und regionale Preisdifferenzen. Der Kompromiss: du brauchst zuverlässige Proxies, weil eBay Datacenter-IPs aggressiv blockiert.
eBay-API-Limits auf einen Blick
| API | Tägliches Limit (Standard) | Auth-Aufwand | Datenabdeckung |
|---|---|---|---|
| Finding API | ~5.000 Calls/Tag | App-Key | Suchergebnisse, Item-IDs |
| Browse API | ~10.000 Calls/Tag | OAuth + App-Key | Item-Details, Preis, Bilder |
| Trading API | ~1.500 Calls/Tag | OAuth + User-Token | Eigene Listings, Orders |
| HTML-Scraping | Kein festes Limit | Proxies | Alles sichtbare + dynamische Daten |
Regel: Nutze die API für kleine, stabile Abfragen. Scalierst du über die Quoten hinaus oder brauchst du Daten, die die API nicht liefert (Auktions-Timer, Verkäufer-Feedback-Verlauf), wechsle zum Scraping mit Residential Proxies.
Die HTML-Struktur von eBay verstehen
Bevor du Code schreibst, musst du wissen, wo die Daten sitzen. eBay nutzt serverseitiges Rendering für die wichtigsten Inhalte – Glück für Scraper, weil die Daten im initialen HTML landen und nicht erst per JavaScript nachgeladen werden.
Suchergebnisse: das .s-item-Grid
Die Suchergebnisseite listet jedes Listing in einem Container mit der Klasse .s-item. Innerhalb jedes Containers findest du:
- Titel:
.s-item__title– Produktname, manchmal mit Zusätzen wie "NEW LISTING" - Preis:
.s-item__price– formatierter Preisstring, z.B. "EUR 129,99" - Versand:
.s-item__shipping– Versandkosten oder "Kostenloser Versand" - Auktions-Info:
.s-item__bid-count(Gebote),.s-item__time-left(Restzeit) - Buy-It-Now:
.s-item__purchase-optionmit Text "Sofort-Kaufen" - Verkäufer:
.s-item__seller-info– Verkäufername und Feedback - URL:
.s-item__link– Link zur Detailseite
Listing-Detailseite
Auf der Detailseite (/itm/ITEM_ID) liegen tiefere Informationen:
- Preis:
[itemprop="price"]oder#prcIsum - Artikelnummer:
#descItemNumberoder[itemprop="productID"] - Verkäufer-Feedback:
.mbg(Verkäufer-Link) +.mbg-l(Feedback-Score) - Auktions-Timer:
#vi-cdown_timeLeft– dynamischer Countdown, serverseitig initialisiert - Gebote:
#bidBtn-Text oder.vi-bid-count
Verkäufer-Profilseite
Unter /usr/SELLER_NAME findest du:
- Feedback-Score:
.mbg-l– Zahl neben dem Stern - Positive-Feedback-%:
#feedback_percent - Mitglied seit:
.mbg-Bereich, Info-Label - Aktive Listings:
.seller-item-cardim "Other items"-Bereich
Proxy-Auswahl: Warum Residential für eBay Pflicht ist
eBay betreibt ein aggressives Anti-Bot-System, das Datacenter-IP-Ranges kennt und blockiert. Wenn du von einem DC-Proxy scrapst, bekommst du nach wenigen Requests CAPTCHAs, Rate-Limit-Fehler oder leere Seiten. Residential Proxies sind hier kein Luxus, sondern Voraussetzung.
Proxy-Typen im Vergleich für eBay
| Proxy-Typ | eBay-Blockier-Risiko | Geschwindigkeit | Bestes Einsatzgebiet |
|---|---|---|---|
| Datacenter | Sehr hoch | Schnell | Nicht empfohlen für eBay |
| Residential (rotierend) | Niedrig | Mittel | Breite Such-Scrapes, Preisüberwachung |
| Residential (Sticky) | Niedrig | Mittel | Auktions-Tracking, Session-basierte Scrapes |
| Mobile | Sehr niedrig | Langsamer | Höchste Vertrauensstufe, seltene Anfragen |
Geo-Targeting für regionale eBay-Domains
eBay zeigt unterschiedliche Preise, Listings und Versandoptionen je nach Standort des Nutzers. Wenn du eBay.de, eBay.co.uk oder eBay.com.au scrapen willst, musst du Proxies aus dem jeweiligen Land verwenden – sonst siehst du entweder umgeleitete Ergebnisse oder regional eingeschränkte Daten.
Mit ProxyHat kannst du das Land direkt im Benutzernamen festlegen:
# Deutschland-IP für eBay.de
http://user-country-DE:pass@gate.proxyhat.com:8080
# UK-IP für eBay.co.uk
http://user-country-GB:pass@gate.proxyhat.com:8080
# Stadtgenau: Berlin für lokalisierte Versanddaten
http://user-country-DE-city-berlin:pass@gate.proxyhat.com:8080Für Auktions-Tracking, bei dem du über Minuten hinweg dieselbe IP brauchst, nutzt du Sticky Sessions:
# 10-Minuten-Session für Auktions-Monitoring
http://user-country-DE-session-auc456:pass@gate.proxyhat.com:8080Python-Beispiel: eBay-Suchergebnisse scrapen und parsen
Hier ist ein vollständiges, funktionierendes Beispiel, das eBay.de nach einem Suchbegriff abfragt, die .s-item-Container parsed und strukturierte Datensätze erzeugt.
import requests
from bs4 import BeautifulSoup
from dataclasses import dataclass
from typing import List
import time
import random
PROXY_URL = "http://user-country-DE:PASSWORD@gate.proxyhat.com:8080"
PROXIES = {"http": PROXY_URL, "https": PROXY_URL}
HEADERS = {
"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"
),
"Accept-Language": "de-DE,de;q=0.9,en;q=0.5",
"Accept": "text/html,application/xhtml+xml",
}
@dataclass
class EbayListing:
title: str
price: str
shipping: str
url: str
bid_count: str | None
time_left: str | None
is_buy_it_now: bool
seller: str
def scrape_ebay_search(query: str, max_pages: int = 3) -> List[EbayListing]:
listings = []
for page in range(1, max_pages + 1):
url = f"https://www.ebay.de/sch/i.html?_nkw={query}&_pgn={page}"
resp = requests.get(url, headers=HEADERS, proxies=PROXIES, timeout=30)
resp.raise_for_status()
soup = BeautifulSoup(resp.text, "html.parser")
items = soup.select(".s-item")
for item in items:
title_el = item.select_one(".s-item__title")
if not title_el or "Shop auf eBay" in title_el.text:
continue # Skip placeholder entries
price_el = item.select_one(".s-item__price")
shipping_el = item.select_one(".s-item__shipping")
link_el = item.select_one(".s-item__link")
bid_el = item.select_one(".s-item__bid-count")
time_el = item.select_one(".s-item__time-left")
bin_el = item.select_one(".s-item__purchase-option")
seller_el = item.select_one(".s-item__seller-info")
listings.append(EbayListing(
title=title_el.text.strip(),
price=price_el.text.strip() if price_el else "N/A",
shipping=shipping_el.text.strip() if shipping_el else "N/A",
url=link_el["href"] if link_el else "",
bid_count=bid_el.text.strip() if bid_el else None,
time_left=time_el.text.strip() if time_el else None,
is_buy_it_now=bin_el is not None and "Sofort" in bin_el.text,
seller=seller_el.text.strip() if seller_el else "N/A",
))
# Rate limiting: 2-5 Sekunden zwischen Seiten
time.sleep(random.uniform(2, 5))
return listings
# Ausführung
results = scrape_ebay_search("mechanische tastatur", max_pages=2)
for r in results[:5]:
print(f"{r.title[:50]}... | {r.price} | Gebote: {r.bid_count} | BIN: {r.is_buy_it_now}")Beispielausgabe (gekürzt)
Logitech G915 TKL Wireless... | EUR 89,99 | Gebote: None | BIN: True
Razer Huntsman V2 Analog... | EUR 124,90 | Gebote: 3 Gebote | BIN: False
Corsair K100 RGB... | EUR 64,50 | Gebote: 7 Gebote | BIN: FalseAuktionen handhaben: Timer, Gebote und Sofort-Kaufen
Auktionsdaten sind besonders wertvoll für Reseller, weil sie Echtzeit-Marktdynamik zeigen. Die Herausforderung: Auktions-Timer werden clientseitig per JavaScript heruntergezählt, aber der Startwert kommt vom Server und steht im initialen HTML.
Strategien für Auktions-Scraping
- Einmaliger Snapshot: Scrape die Suchergebnisseite und extrahiere
.s-item__time-leftplus.s-item__bid-count. Gut für Trend-Analysen über viele Listings. - Polling mit Sticky Sessions: Nutze eine Session-IP und frage die Detailseite alle 30–60 Sekunden ab. Der Countdown-Wert im HTML wird serverseitig aktualisiert.
- Endpreis-Erfassung: Scrape abgeschlossene Listings unter
/sch/i.html?_nkw=QUERY&LH_Complete=1&LH_Sold=1, um tatsächliche Verkaufspreise zu erhalten.
Hier ein Snippet für abgeschlossene Listings:
def scrape_completed_listings(query: str) -> List[dict]:
"""Scrape abgeschlossene eBay-Listings für Preis-Trendanalyse."""
url = (
f"https://www.ebay.de/sch/i.html?"
f"_nkw={query}&LH_Complete=1&LH_Sold=1&_pgn=1"
)
resp = requests.get(url, headers=HEADERS, proxies=PROXIES, timeout=30)
soup = BeautifulSoup(resp.text, "html.parser")
results = []
for item in soup.select(".s-item"):
title = item.select_one(".s-item__title")
price = item.select_one(".s-item__price")
sold_status = item.select_one(".s-item__purchase-option")
if title and price and "Shop auf eBay" not in title.text:
results.append({
"title": title.text.strip(),
"sold_price": price.text.strip(),
"sold": sold_status is not None and "verkauft" in sold_status.text.lower(),
})
return results
# Verwendung
completed = scrape_completed_listings("airpods pro 2")
print(f"{len(completed)} verkaufte Listings gefunden")Verkäufer-Analyse: Feedback, Kategorien und Cross-Listing-Muster
Für Competitive Intelligence ist die Verkäufer-Ebene oft wertvoller als einzelne Listings. Du willst wissen: Wer verkauft am meisten? Welche Kategorien dominieren sie? Wie cross-listen sie Produkte?
Schritt-für-Schritt-Verkäufer-Analyse
- Verkäufer-IDs aus Suchergebnissen sammeln – extrahiere den Verkäufername aus
.s-item__seller-infooder der Detailseite. - Verkäufer-Profil scrapen – rufe
/usr/SELLER_NAMEauf und extrahiere Feedback-Score und Positive-Rate. - Andere Listings des Verkäufers erfassen – der "Andere Artikel"-Bereich zeigt aktive Listings desselben Verkäufers.
- Kategorien aggregieren – zähle, in welchen Kategorien ein Verkäufer aktiv ist, um sein Geschäftsmodell zu verstehen.
def scrape_seller_profile(seller_name: str) -> dict:
"""Extrahiere Verkäufer-Metadaten vom Profil."""
url = f"https://www.ebay.de/usr/{seller_name}"
resp = requests.get(url, headers=HEADERS, proxies=PROXIES, timeout=30)
soup = BeautifulSoup(resp.text, "html.parser")
feedback_score_el = soup.select_one(".mbg-l")
feedback_pct_el = soup.select_one("#feedback_percent")
member_since_el = soup.select_one(".mbg .info")
# Aktive Listings des Verkäufers
seller_items = []
for card in soup.select(".seller-item-card"):
title_el = card.select_one(".seller-item-card__title")
price_el = card.select_one(".seller-item-card__price")
if title_el and price_el:
seller_items.append({
"title": title_el.text.strip(),
"price": price_el.text.strip(),
})
return {
"seller": seller_name,
"feedback_score": feedback_score_el.text.strip() if feedback_score_el else "N/A",
"positive_pct": feedback_pct_el.text.strip() if feedback_pct_el else "N/A",
"member_since": member_since_el.text.strip() if member_since_el else "N/A",
"active_listings_count": len(seller_items),
"sample_listings": seller_items[:5],
}
# Verwendung
profile = scrape_seller_profile("topseller_electronics")
print(f"Feedback: {profile['feedback_score']} | Positiv: {profile['positive_pct']}")
print(f"Aktive Listings: {profile['active_listings_count']}")Cross-Listing-Muster erkennen
Um zu verstehen, ob ein Verkäufer in mehreren Nischen aktiv ist, scrapst du die Suchergebnisse für verschiedene Keywords, sammelst die Verkäufer-Namen und zählst, wie oft jeder Verkäufer in welchen Suchergebnissen auftaucht. Das ergibt eine Verkäufer-Kategorie-Matrix, die du in eine Pivot-Tabelle oder Heatmap umwandeln kannst.
eBays Anti-Bot-Technologie und wie du sie umgehst
eBay nutzt mehrere Verteidigungslinien:
- IP-Reputation: Datacenter-IP-Ranges werden direkt blockiert oder mit CAPTCHAs belegt.
- Rate-Limiting: Mehr als ~100 Requests pro Minute von derselben IP lösen Temp-Blocks aus (HTTP 429 oder leere Seiten).
- Browser-Fingerprinting: eBay prüft JavaScript-Fähigkeiten, Canvas-Rendering und TLS-Fingerabdrücke. Reine HTTP-Requests ohne passenden User-Agent fallen auf.
- Verhaltensanalyse: Seitenaufrufe in zu regelmäßigen Intervallen oder zu schnelle Navigation durch Paginierung werden erkannt.
Gegenmaßnahmen:
- Residential Proxies mit Rotation – neue IP pro Request oder alle 5 Requests.
- Realistische User-Agent-Strings – rotiere zwischen aktuellen Chrome/Firefox-Versionen.
- Zufällige Verzögerungen – 2–8 Sekunden zwischen Requests, nicht konstant.
- Session-Konsistenz wo nötig – Sticky Sessions für Auktions-Tracking, damit die IP-Wechsel nicht auffallen.
Node.js-Beispiel: eBay mit SOCKS5 scrapen
Für Node.js-Umgebungen bietet SOCKS5 manchmal bessere Kompatibilität. Hier ein Minimalbeispiel mit socks-proxy-agent:
const axios = require('axios');
const { SocksProxyAgent } = require('socks-proxy-agent');
const agent = new SocksProxyAgent(
'socks5://user-country-DE:PASSWORD@gate.proxyhat.com:1080'
);
async function fetchEbayPage(url) {
const resp = await axios.get(url, {
httpsAgent: agent,
headers: {
'User-Agent':
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ' +
'AppleWebKit/537.36 (KHTML, like Gecko) ' +
'Chrome/125.0.0.0 Safari/537.36',
'Accept-Language': 'de-DE,de;q=0.9',
},
timeout: 30000,
});
return resp.data;
}
// Verwendung
fetchEbayPage('https://www.ebay.de/sch/i.html?_nkw=mechanische+tastatur')
.then(html => console.log('HTML-Länge:', html.length))
.catch(err => console.error('Fehler:', err.message));curl: Schneller Test mit ProxyHat
Bevor du ein volles Skript schreibst, teste die Verbindung manuell:
# HTTP-Proxy
curl -x http://user-country-DE:PASSWORD@gate.proxyhat.com:8080 \
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/125.0.0.0" \
"https://www.ebay.de/sch/i.html?_nkw=iphone+15" 2>/dev/null | grep -c 's-item'
# SOCKS5-Proxy
curl -x socks5://user-country-DE:PASSWORD@gate.proxyhat.com:1080 \
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/125.0.0.0" \
"https://www.ebay.de/sch/i.html?_nkw=iphone+15" 2>/dev/null | grep -c 's-item'Wenn die Ausgabe eine Zahl > 0 liefert, funktioniert die Verbindung und du siehst Suchergebnisse. Bei 0 oder einer CAPTCHA-Seite musst du den Proxy-Typ oder die Geo-Einstellung prüfen.
Ethische und rechtliche Überlegungen
eBays Terms of Use verbieten automatisiertes Scraping ausdrücklich. In der Praxis scrapen Teams trotzdem – aber sie sollten dabei verantwortungsvoll vorgehen:
- Respektiere robots.txt: Prüfe
https://www.ebay.de/robots.txtfür gesperrte Pfade. - Rate-Limits einhalten: Auch mit Proxies – nicht mehr als 1 Request pro 2–3 Sekunden pro IP.
- Keine persönlichen Daten speichern: Verkäufer-Namen sind geschäftlich, aber vermeide die Speicherung von Käufer-Daten unter DSGVO/GDPR.
- Daten nur für legitime Zwecke: Marktforschung und Preisvergleich sind vertretbar; Betrug oder Manipulation nicht.
Key Takeaways
- Die eBay-API reicht für kleine Abfragen – für breite Scrapes brauchst du HTML-Extraktion mit Residential Proxies.
- Die
.s-item-Klasse ist dein Haupteinstiegspunkt für Suchergebnisse; Detailseiten liefern Auktions-Timer und Verkäufer-Feedback.- Residential Proxies sind Pflicht – Datacenter-IPs werden auf eBay innerhalb weniger Requests blockiert.
- Geo-Targeting ist entscheidend für regionale Domains wie eBay.de und eBay.co.uk – die Preise und Listings unterscheiden sich je nach Standort.
- Auktionsdaten erfordern Sticky Sessions für Polling; abgeschlossene Listings liefern tatsächliche Verkaufspreise.
- Verkäufer-Analyse (Feedback, Kategorien, Cross-Listing) liefert strategische Insights, die die API nicht bietet.
Fazit und nächste Schritte
eBay scrapen ist kein Hexenwerk – aber es erfordert die richtige Infrastruktur. Mit Residential Proxies aus ProxyHat, realistischen Headers und sauberem Parsing der .s-item-Struktur bekommst du zuverlässig Zugriff auf Suchergebnisse, Auktionsdaten und Verkäufer-Profile. Starte klein mit einem curl-Test, skaliere dann mit dem Python- oder Node.js-Beispiel, und passe die Rate-Limits an dein Volumen an.
Bereit loszulegen? Hol dir deine ProxyHat-Zugangsdaten unter ProxyHat Pricing und teste die Beispiele aus diesem Artikel. Für tiefergehende Scraping-Strategien sieh dir unseren Guide zu Web-Scraping-Best-Practices und die Web-Scraping-Anwendungsfälle an.






