Sie wollen wissen, ob ein Produkt verfügbar ist, wie sich sein Preis entwickelt und wann die Konkurrenz nachzieht – doch die Produktseite blockt automatisierte Zugriffe mit einem reCAPTCHA oder einer Cloudflare-Turnstile-Abfrage. Dieser Leitfaden zeigt, wie Sie Preise, Lagerbestand und Verfügbarkeit im Online-Handel dauerhaft überwachen, während CaptchaAI die CAPTCHA-Abfragen im Hintergrund löst. Große Händler wie Amazon, Otto oder MediaMarkt spielen CAPTCHAs gezielt aus, sobald ein Zugriffsmuster nach Bot aussieht – die folgenden Python-Bausteine halten Ihre Monitoring-Pipeline trotzdem am Laufen.
CAPTCHAs im Online-Handel: Wo sie auftauchen
Bevor Sie eine Zeile Code schreiben, lohnt der Blick darauf, welche Schutzmechanismen auf welchen Plattformen greifen. Die folgende Übersicht stammt aus dem US-Handel, das Muster gilt aber genauso für DACH-Händler wie Otto, Zalando, MediaMarkt oder Preisvergleiche wie Idealo: Produktsuche, Warenkorb-Prüfung und hochfrequente Zugriffe sind die typischen Auslöser.
| Plattform | CAPTCHA-Typ | Auslöser | Daten |
|---|---|---|---|
| Amazon | reCAPTCHA v2 | Zugriffe mit hohem Volumen | Preis, Lagerbestand, Bewertungen |
| Walmart | reCAPTCHA v3 + Cloudflare | Bot-Erkennung | Bestand, Preise |
| Best Buy | reCAPTCHA v2 | Add-to-Cart-Prüfung | Lagerbestand, Preise |
| Target | Cloudflare Turnstile | Automatisierter Zugriff | Verfügbarkeit |
| Shopify-Shops | Cloudflare Turnstile | Rate-Limiting | Produktdaten |
| eBay | reCAPTCHA v2 | Suche + Listing-Zugriff | Angebote, Preise |
CaptchaAI löst die hier relevanten Typen – reCAPTCHA v2, reCAPTCHA v3 und Cloudflare Turnstile – über eine einzige API. hCaptcha und FunCaptcha werden nicht unterstützt; falls eine Zielseite darauf umstellt, greifen die Beispiele unten nicht.
Prüfintervall und Proxy-Wahl für die Bestandsüberwachung
Die wichtigste Entscheidung fällt vor dem ersten Request: Wie oft prüfen Sie, und über welche Proxys? Zu aggressive Intervalle provozieren mehr CAPTCHAs und IP-Sperren, zu träge Intervalle verpassen kurzfristige Preis- oder Verfügbarkeitsänderungen. Als Ausgangspunkt hat sich diese Staffelung nach Produkttyp bewährt:
| Produkttyp | Prüfintervall | Proxy-Typ |
|---|---|---|
| Elektronik | alle 30 Min | rotierender Residential-Proxy |
| Lebensmittel | alle 4 Stunden | rotierender Residential-Proxy |
| Mode | alle 2 Stunden | Residential-Proxy |
| Limitierte Drops | jede Minute | Mobiler Proxy |
| Haushaltswaren | alle 6 Stunden | Residential-Proxy |
| Standardware | alle 12 Stunden | Rechenzentrums-Proxy (oft ausreichend) |
Residential- und Mobile-Proxys sind teurer, lösen aber deutlich seltener eine CAPTCHA-Abfrage aus als Rechenzentrums-IPs. Für Standardware mit geringem Schutz reichen letztere; bei limitierten Releases mit Minutentakt führt an Mobile-Proxys kaum ein Weg vorbei.
Produktseiten überwachen: der RetailMonitor
Der Kern ist eine RetailMonitor-Klasse, die eine Produktseite abruft, eine eventuelle CAPTCHA-Abfrage über CaptchaAI löst, den Request wiederholt und anschließend Titel, Preis und Verfügbarkeit aus dem HTML extrahiert. Die Methode monitor_products läuft in einer Schleife und meldet nur, wenn sich Preis oder Lagerstatus gegenüber dem letzten Zyklus geändert haben.
import requests
import time
import re
import json
from datetime import datetime
from bs4 import BeautifulSoup
CAPTCHAAI_KEY = "YOUR_API_KEY"
CAPTCHAAI_URL = "https://ocr.captchaai.com"
def solve_captcha(method, sitekey, pageurl, **kwargs):
data = {
"key": CAPTCHAAI_KEY, "method": method,
"googlekey": sitekey, "pageurl": pageurl, "json": 1,
}
data.update(kwargs)
resp = requests.post(f"{CAPTCHAAI_URL}/in.php", data=data)
task_id = resp.json()["request"]
for _ in range(60):
time.sleep(5)
result = requests.get(f"{CAPTCHAAI_URL}/res.php", params={
"key": CAPTCHAAI_KEY, "action": "get",
"id": task_id, "json": 1,
})
r = result.json()
if r["request"] != "CAPCHA_NOT_READY":
return r["request"]
raise TimeoutError("Timeout")
class RetailMonitor:
def __init__(self, proxy=None):
self.session = requests.Session()
if proxy:
self.session.proxies = {"http": proxy, "https": proxy}
self.session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 Chrome/126.0.0.0 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.9",
})
def check_product(self, url):
"""Check single product's price and availability."""
resp = self.session.get(url, timeout=30)
# Handle CAPTCHA
if self._has_captcha(resp.text):
resp = self._solve_and_retry(resp.text, url)
soup = BeautifulSoup(resp.text, "html.parser")
return {
"url": url,
"title": self._text(soup, "h1, .product-title, #productTitle"),
"price": self._text(soup, ".price, .a-price .a-offscreen, .prod-price"),
"availability": self._text(soup, "#availability, .stock-status, .fulfillment"),
"in_stock": self._check_stock(soup),
"timestamp": datetime.now().isoformat(),
}
def monitor_products(self, product_urls, interval_sec=1800):
"""Continuously monitor products for changes."""
history = {}
while True:
for url in product_urls:
try:
current = self.check_product(url)
# Check for changes
prev = history.get(url)
if prev:
changes = self._detect_changes(prev, current)
if changes:
self._alert(current["title"], changes)
history[url] = current
time.sleep(3)
except Exception as e:
print(f"Error checking {url}: {e}")
print(f"Cycle complete: {len(product_urls)} products checked")
time.sleep(interval_sec)
def track_prices(self, product_urls, output_file="prices.json"):
"""Single price check across all products."""
results = []
for url in product_urls:
try:
data = self.check_product(url)
results.append(data)
time.sleep(3)
except Exception as e:
results.append({"url": url, "error": str(e)})
with open(output_file, "w") as f:
json.dump(results, f, indent=2)
print(f"Tracked {len(results)} products → {output_file}")
return results
def _has_captcha(self, html):
return any(tag in html.lower() for tag in [
'data-sitekey', 'g-recaptcha', 'cf-turnstile', 'captcha',
])
def _solve_and_retry(self, html, url):
match = re.search(r'data-sitekey="([^"]+)"', html)
if not match:
return self.session.get(url)
sitekey = match.group(1)
if 'cf-turnstile' in html:
token = solve_captcha("turnstile", sitekey, url)
return self.session.post(url, data={"cf-turnstile-response": token})
else:
token = solve_captcha("userrecaptcha", sitekey, url)
return self.session.post(url, data={"g-recaptcha-response": token})
def _text(self, soup, selector):
el = soup.select_one(selector)
return el.get_text(strip=True) if el else ""
def _check_stock(self, soup):
stock_el = soup.select_one("#availability, .stock-status")
if stock_el:
text = stock_el.get_text(strip=True).lower()
return "in stock" in text or "available" in text
return None
def _detect_changes(self, prev, current):
changes = []
if prev["price"] != current["price"]:
changes.append(f"Price: {prev['price']} → {current['price']}")
if prev["in_stock"] != current["in_stock"]:
status = "In Stock" if current["in_stock"] else "Out of Stock"
changes.append(f"Stock: → {status}")
return changes
def _alert(self, title, changes):
print(f"ALERT [{title}]: {', '.join(changes)}")
# Usage
monitor = RetailMonitor(
proxy="http://user:pass@residential.proxy.com:5000"
)
products = [
"https://store.example.com/product/abc123",
"https://store.example.com/product/def456",
"https://store.example.com/product/ghi789",
]
# One-time price check
results = monitor.track_prices(products)
# Or continuous monitoring (every 30 min)
# monitor.monitor_products(products, interval_sec=1800)
Der Ablauf ist bei jeder Zielseite gleich: _has_captcha erkennt Sitekey- und Turnstile-Marker im HTML, _solve_and_retry liest den data-sitekey aus, wählt anhand des Markers zwischen turnstile und userrecaptcha und reicht das gelöste Token als cf-turnstile-response oder g-recaptcha-response wieder ein. So bleibt der Rest Ihrer Logik von der CAPTCHA-Verarbeitung getrennt.
Ganze Kategorien nach Lagerbestand scannen
Für einzelne SKUs reicht check_product. Wollen Sie dagegen eine komplette Kategorie erfassen – etwa alle Notebooks eines Shops –, blättern Sie seitenweise durch die Listing-Seiten. Die Funktion nutzt dieselbe RetailMonitor-Instanz und bricht ab, sobald eine Seite keine Treffer mehr liefert.
def scan_category(base_url, category, max_pages=20):
"""Scan an entire product category for stock status."""
monitor = RetailMonitor(
proxy="http://user:pass@residential.proxy.com:5000"
)
all_products = []
for page in range(1, max_pages + 1):
url = f"{base_url}/{category}?page={page}"
resp = monitor.session.get(url, timeout=30)
if monitor._has_captcha(resp.text):
resp = monitor._solve_and_retry(resp.text, url)
soup = BeautifulSoup(resp.text, "html.parser")
items = soup.select(".product-card, .s-result-item")
if not items:
break
for item in items:
all_products.append({
"name": monitor._text(item, ".product-name, .a-text-normal"),
"price": monitor._text(item, ".price, .a-price"),
"stock": monitor._text(item, ".stock, .a-color-success"),
"url": item.select_one("a")["href"] if item.select_one("a") else "",
})
time.sleep(3)
return all_products
Passen Sie die CSS-Selektoren an das Markup der jeweiligen Zielseite an – die hier genutzten Klassen decken das Amazon-typische Listing-Layout ab, ein Shopware- oder JTL-Shop verwendet andere.
Preise mehrerer Händler vergleichen
Der klassische Anwendungsfall im DACH-Raum ist der Preisvergleich: dasselbe Produkt bei mehreren Händlern suchen und nach Preis sortieren – im Prinzip das, was Idealo oder Geizhals automatisiert tun. Jeder Store bekommt seinen eigenen Proxy, damit die parallelen Anfragen nicht auf eine IP zurückfallen.
def compare_product_across_stores(product_name, stores):
"""Compare prices across retailers for the same product."""
results = []
for store in stores:
monitor = RetailMonitor(proxy=store.get("proxy"))
search_url = f"{store['base_url']}/search?q={product_name}"
try:
resp = monitor.session.get(search_url, timeout=30)
if monitor._has_captcha(resp.text):
resp = monitor._solve_and_retry(resp.text, search_url)
soup = BeautifulSoup(resp.text, "html.parser")
first_result = soup.select_one(".product-card, .s-result-item")
if first_result:
results.append({
"store": store["name"],
"price": monitor._text(first_result, ".price"),
"in_stock": "in stock" in first_result.get_text().lower(),
})
except Exception as e:
results.append({"store": store["name"], "error": str(e)})
time.sleep(5)
results.sort(key=lambda x: x.get("price", "zzzz"))
return results
Typische Fehler bei der Bestandsüberwachung beheben
Die meisten Probleme im Dauerbetrieb lassen sich einer der folgenden Ursachen zuordnen:
| Problem | Ursache | Lösung |
|---|---|---|
| CAPTCHA auf jeder Produktseite | IP markiert oder Rate überschritten | Verzögerung erhöhen, Proxys rotieren |
| Falscher Preis ausgelesen | Preise werden per JavaScript nachgeladen | stattdessen Selenium/Puppeteer verwenden |
| „Robot-Check"-Seite | Bot-Erkennung im Amazon-Stil | realistische Header + Residential-Proxy nutzen |
| Lagerbestand „nicht verfügbar" | geografisch eingeschränkte Verfügbarkeit | Proxy der Zielregion zuordnen |
| Fehlende Produktdaten | Seitenstruktur hat sich geändert | CSS-Selektoren aktualisieren |
Wenn Preise per JavaScript gerendert werden, hilft kein reines HTML-Parsing – hier führt der Weg über einen Headless-Browser wie Selenium oder Puppeteer, der CaptchaAI genauso einbinden kann.
Rechtlicher Hinweis: Preisüberwachung und DSGVO
Preis- und Verfügbarkeitsdaten sind öffentlich einsehbar, das automatisierte Auslesen im großen Stil bewegt sich rechtlich aber in einem sensiblen Bereich. Prüfen Sie vorab die AGB der Zielseite und Ihre eigene Rechtsgrundlage. Sobald Sie über Proxys arbeiten, verarbeiten Sie IP-Adressen – nach DSGVO personenbezogene Daten. Dokumentieren Sie Ihre Datenflüsse, beschränken Sie die Erfassung auf das tatsächlich Benötigte und halten Sie für vergleichende Aussagen (Stichwort UWG § 6) belastbare, datierte Messwerte bereit.
Häufige Fragen
Welche CAPTCHA-Typen tauchen bei der Bestandsüberwachung auf?
Meist reCAPTCHA v2, reCAPTCHA v3 und Cloudflare Turnstile – alle drei löst CaptchaAI über dieselbe API. hCaptcha und FunCaptcha werden nicht unterstützt; stellt eine Zielseite darauf um, greifen die hier gezeigten Beispiele nicht.
Was kostet die Überwachung vieler Produkte mit CaptchaAI?
CaptchaAI rechnet pro gleichzeitigem Thread ab, nicht pro Lösung – die Zahl der Solves pro Monat ist unbegrenzt. Für kleinere Monitoring-Jobs reicht der BASIC-Tarif (15 $/Monat, 5 Threads); wer Tausende Produkte parallel prüft, fährt mit ADVANCE (90 $/Monat, 50 Threads) besser. Preise in US-Dollar.
Warum lese ich manchmal falsche oder veraltete Preise aus?
Fast immer, weil der Preis per JavaScript nachgeladen wird und im rohen HTML noch nicht steht. Prüfen Sie den tatsächlichen HTML-Quelltext; wenn der Wert fehlt, wechseln Sie auf Selenium oder Puppeteer statt reinem Requests-Parsing.
Kann ich Tausende Produkte gleichzeitig überwachen?
Ja. Verteilen Sie die Anfragen auf mehrere Proxy-IPs und versetzen Sie die Prüfungen zeitlich. Bei 1.000 Produkten mit 3-Sekunden-Abstand dauert ein vollständiger Zyklus rund 50 Minuten – mehr Threads und Proxys verkürzen das entsprechend.
Weiterführende Anleitungen
- Rotierende Residential-Proxys richtig einsetzen
- Sticky- oder rotierende Sitzungen wählen
- Lieferketten automatisiert überwachen
Überwachen Sie Ihren Einzelhandelsbestand in Echtzeit – Holen Sie sich Ihren CaptchaAI-Schlüssel für die automatisierte CAPTCHA-Verarbeitung.