Anwendungsfälle

Auktionsseiten überwachen: reCAPTCHA v2 automatisch lösen

Wer Gebote, Preisverläufe und neue Einträge auf Auktionsportalen automatisch verfolgen will, stößt früher oder später auf reCAPTCHA v2 – genau bei den Aktionen, die ein Monitoring-Skript am häufigsten ausführt: schnelle Suchabfragen, wiederholte Detailansichten, das Nachladen der Gebotshistorie. Der Ablauf, der das zuverlässig löst, ist immer derselbe: CAPTCHA im Antwort-HTML erkennen, über CaptchaAI lösen lassen und die Anfrage mit dem Token erneut senden. Ihr Scraper läuft weiter, ohne dass jemand eingreifen muss.

Wann Auktionsportale ein CAPTCHA anzeigen

Ausgelöst wird die Abfrage fast immer durch das Muster der Zugriffe, nicht durch die einzelne Anfrage. reCAPTCHA v2 ist der Standardschutz, gelegentlich ergänzt durch Cloudflare Turnstile in der Kategorie-Navigation. Wer die typischen Auslöser kennt, kann sein Monitoring so takten, dass CAPTCHAs seltener erscheinen – und wenn doch, greift der automatische Lösungsweg.

Aktion CAPTCHA-Typ Auslöser
Einträge suchen und durchblättern reCAPTCHA v2 Schnelle Suchabfragen in Folge
Eintragsdetails öffnen reCAPTCHA v2 Hohes Volumen von derselben IP
Gebotshistorie abrufen reCAPTCHA v2 Detailseite wiederholt geladen
Kategorien durchsuchen Cloudflare Turnstile Bot-typische Navigationsgeschwindigkeit
Preisalarm-Seiten reCAPTCHA v2 Häufige Aktualisierungen

In vier Schritten zur laufenden Überwachung

Der Kern jeder Auktionsüberwachung folgt derselben Reihenfolge – unabhängig davon, ob Sie in Python oder JavaScript arbeiten:

  1. Ziel-URLs und Suchbegriffe festlegen und je Quelle eine eigene Session anlegen.
  2. Das Antwort-HTML auf reCAPTCHA v2 prüfen und bei Bedarf den Sitekey extrahieren.
  3. Das Token über CaptchaAI lösen und die Anfrage in derselben Sitzung erneut senden.
  4. Erst danach die Einträge parsen, Ergebnisse protokollieren und das nächste Intervall abwarten.

Auktionen überwachen und CAPTCHAs automatisch lösen

Die folgende Klasse kapselt Suche, Detailabruf und Gebotsverfolgung: Trifft eine Anfrage auf reCAPTCHA v2, wird der Sitekey extrahiert, das Token über CaptchaAI gelöst und die Anfrage in derselben Sitzung wiederholt. Entscheidend ist, dass Suche und Detailabruf dieselbe requests.Session() nutzen – so bleiben Cookies erhalten und das nächste CAPTCHA lässt oft länger auf sich warten.

import requests
import time
import re
from datetime import datetime

class AuctionMonitor:
    def __init__(self, api_key):
        self.api_key = api_key
        self.session = requests.Session()
        self.session.headers.update({
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
        })

    def search_listings(self, auction_url, query, category=None):
        """Search auction listings, solving CAPTCHAs when triggered."""
        params = {"q": query}
        if category:
            params["category"] = category

        response = self.session.get(
            f"{auction_url}/search", params=params
        )

        if self._has_captcha(response.text):
            site_key = self._extract_site_key(response.text)
            token = self._solve_recaptcha(site_key, f"{auction_url}/search")
            response = self.session.post(
                f"{auction_url}/search",
                data={**params, "g-recaptcha-response": token}
            )

        return self._parse_listings(response.text)

    def monitor_listing(self, auction_url, listing_id):
        """Get current bid and listing details."""
        url = f"{auction_url}/item/{listing_id}"
        response = self.session.get(url)

        if self._has_captcha(response.text):
            site_key = self._extract_site_key(response.text)
            token = self._solve_recaptcha(site_key, url)
            response = self.session.post(url, data={
                "g-recaptcha-response": token
            })

        return self._parse_listing_detail(response.text)

    def track_bids(self, auction_url, listing_ids, interval=60):
        """Track bid changes across multiple listings."""
        history = {lid: [] for lid in listing_ids}

        while True:
            for listing_id in listing_ids:
                try:
                    detail = self.monitor_listing(auction_url, listing_id)
                    previous = history[listing_id]

                    if previous and detail["current_bid"] != previous[-1]["current_bid"]:
                        print(f"Bid change on {listing_id}: "
                              f"${previous[-1]['current_bid']} → ${detail['current_bid']}")

                    history[listing_id].append(detail)
                except Exception as e:
                    print(f"Error checking {listing_id}: {e}")

            time.sleep(interval)

    def _has_captcha(self, html):
        return "g-recaptcha" in html or "recaptcha" in html.lower()

    def _extract_site_key(self, html):
        match = re.search(r'data-sitekey="([^"]+)"', html)
        if match:
            return match.group(1)
        match = re.search(r"sitekey['\"]?\s*[:=]\s*['\"]([^'\"]+)", html)
        if match:
            return match.group(1)
        raise ValueError("Could not find reCAPTCHA site key")

    def _solve_recaptcha(self, site_key, page_url):
        resp = requests.post("https://ocr.captchaai.com/in.php", data={
            "key": self.api_key,
            "method": "userrecaptcha",
            "googlekey": site_key,
            "pageurl": page_url,
            "json": 1
        })
        task_id = resp.json()["request"]

        for _ in range(60):
            time.sleep(3)
            result = requests.get("https://ocr.captchaai.com/res.php", params={
                "key": self.api_key,
                "action": "get",
                "id": task_id,
                "json": 1
            })
            data = result.json()
            if data["status"] == 1:
                return data["request"]

        raise TimeoutError("reCAPTCHA solve timed out")

    def _parse_listings(self, html):
        from bs4 import BeautifulSoup
        soup = BeautifulSoup(html, "html.parser")

        def text_or_none(node):
            return node.text.strip() if node and node.text else None

        def attr_or_none(node, attr):
            return node.get(attr) if node else None

        listings = []
        for item in soup.select(".listing-item, .auction-item"):
            listings.append({
                "title": text_or_none(item.select_one(".title")),
                "current_bid": text_or_none(item.select_one(".price, .bid")),
                "time_left": text_or_none(item.select_one(".time-left")),
                "url": attr_or_none(item.select_one("a"), "href")
            })
        return listings

    def _parse_listing_detail(self, html):
        from bs4 import BeautifulSoup
        soup = BeautifulSoup(html, "html.parser")

        def text_or_none(node):
            return node.text.strip() if node and node.text else None

        return {
            "title": text_or_none(soup.select_one("h1, .item-title")),
            "current_bid": text_or_none(soup.select_one(".current-bid, .price")),
            "bid_count": text_or_none(soup.select_one(".bid-count")),
            "time_left": text_or_none(soup.select_one(".time-remaining")),
            "checked_at": datetime.now().isoformat()
        }

# Usage
monitor = AuctionMonitor("YOUR_API_KEY")
listings = monitor.search_listings(
    "https://auctions.example.com",
    "vintage electronics",
    category="collectibles"
)

Preisalarme in JavaScript umsetzen

Für ereignisgesteuerte Alarme genügt ein schlanker Tracker: Er beobachtet eine Liste von Einträgen und meldet, sobald ein Gebot in die Nähe Ihrer Preisgrenze rückt – hier bei 90 % des Höchstgebots. Auch dieser Tracker erkennt ein reCAPTCHA v2, löst es über CaptchaAI und wiederholt die Anfrage, bevor er die Detailseite auswertet.

class AuctionTracker {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.watchList = new Map();
  }

  addWatch(listingId, url, maxPrice) {
    this.watchList.set(listingId, { url, maxPrice, history: [] });
  }

  async checkAll() {
    const alerts = [];

    for (const [id, watch] of this.watchList) {
      try {
        const detail = await this.fetchListing(watch.url);
        watch.history.push(detail);

        const price = parseFloat(detail.currentBid.replace(/[^0-9.]/g, ''));
        if (price >= watch.maxPrice * 0.9) {
          alerts.push({
            listing: id,
            price,
            threshold: watch.maxPrice,
            message: `Price approaching limit: $${price} / $${watch.maxPrice}`
          });
        }
      } catch (error) {
        alerts.push({ listing: id, error: error.message });
      }
    }

    return alerts;
  }

  async fetchListing(url) {
    const response = await fetch(url);
    const html = await response.text();

    if (html.includes('g-recaptcha')) {
      return this.solveAndFetch(url, html);
    }

    return this.parseDetail(html);
  }

  async solveAndFetch(url, html) {
    const siteKeyMatch = html.match(/data-sitekey="([^"]+)"/);
    if (!siteKeyMatch) throw new Error('No reCAPTCHA site key found');

    const submitResp = await fetch('https://ocr.captchaai.com/in.php', {
      method: 'POST',
      body: new URLSearchParams({
        key: this.apiKey,
        method: 'userrecaptcha',
        googlekey: siteKeyMatch[1],
        pageurl: url,
        json: '1'
      })
    });

    const { request: taskId } = await submitResp.json();

    for (let i = 0; i < 60; i++) {
      await new Promise(r => setTimeout(r, 3000));
      const result = await fetch(
        `https://ocr.captchaai.com/res.php?key=${this.apiKey}&action=get&id=${taskId}&json=1`
      );
      const data = await result.json();
      if (data.status === 1) {
        // Resubmit with token
        const response = await fetch(url, {
          method: 'POST',
          body: new URLSearchParams({ 'g-recaptcha-response': data.request })
        });
        return this.parseDetail(await response.text());
      }
    }

    throw new Error('reCAPTCHA solve timed out');
  }

  parseDetail(html) {
    // Parse auction listing details from HTML
    return {
      currentBid: html.match(/current.?bid[^>]*>([^<]+)/i)?.[1]?.trim(),
      bidCount: html.match(/(\d+)\s*bids?/i)?.[1],
      timeLeft: html.match(/time.?(?:left|remaining)[^>]*>([^<]+)/i)?.[1]?.trim(),
      checkedAt: new Date().toISOString()
    };
  }
}

// Usage
const tracker = new AuctionTracker('YOUR_API_KEY');
tracker.addWatch('item-123', 'https://auctions.example.com/item/123', 500);
tracker.addWatch('item-456', 'https://auctions.example.com/item/456', 200);
const alerts = await tracker.checkAll();

Die Methode track_bids aus dem Python-Beispiel ergänzt diesen Ansatz um eine Verlaufsprüfung: Sie hält je Eintrag eine Historie vor und meldet nur echte Gebotsänderungen. In Kombination lassen sich so Preisgrenzen (JavaScript) und Gebotsdynamik (Python) getrennt überwachen, ohne bei jedem Durchlauf die volle Detailseite neu zu parsen.

Prüfintervall und erwartete CAPTCHA-Last

Wie oft ein CAPTCHA erscheint, hängt direkt vom Prüfintervall ab. Je enger der Takt, desto eher werten Auktionsportale die Zugriffe als automatisiert – und desto häufiger schaltet sich reCAPTCHA v2 dazwischen. Wählen Sie das gröbste Intervall, das Ihr Anwendungsfall noch zulässt.

Prüfintervall Anwendungsfall Erwartete CAPTCHA-Rate
Alle 30 Sekunden Gebote in letzter Minute Hoch – Proxys einsetzen
Alle 5 Minuten Aktive Auktionsverfolgung Mäßig
Alle 15 Minuten Beobachtungsliste im Blick behalten Niedrig
Stündlich Langfristige Preisrecherche Minimal

CAPTCHAs von vornherein reduzieren

Jede vermiedene Abfrage spart Threads und Laufzeit; die wirksamsten Hebel setzen beim Zugriffsmuster an.

Technik Wirkung
Session-Cookies wiederverwenden Hält den angemeldeten Zustand aufrecht
Residential-Proxys rotieren Verteilt Anfragen auf mehrere IPs
Anfrageintervalle zufällig variieren Vermeidet periodische Erkennungsmuster
Angemeldete Konten nutzen Niedrigere CAPTCHA-Schwellen

Ein Hinweis für den DACH-Raum: Sobald Sie Residential-Proxys einsetzen, verarbeiten Sie fremde IP-Adressen – nach DSGVO ein personenbezogenes Datum. Prüfen Sie vorab Ihre Rechtsgrundlage und die Nutzungsbedingungen des jeweiligen Auktionsportals, bevor Sie ein Monitoring produktiv schalten. Für Worker und geplante Jobs bieten sich Anbieter wie Hetzner oder netcup an; die Zeitpläne lassen sich bequem über GitLab CI oder GitHub Actions steuern.

Typische Probleme beim Auktions-Monitoring

Problem Ursache Lösung
Zu viele CAPTCHAs in kurzer Zeit Abrufrate oder Parallelität ist für die Quelle zu aggressiv Intervalle drosseln, Sessions stabil halten und die Proxy-Qualität prüfen
Trotz gelöstem CAPTCHA fehlen Daten Der Parser liest eine veraltete oder unvollständige Ansicht aus Daten erst nach erfolgreicher Token-Übermittlung in derselben Sitzung auslesen
reCAPTCHA-Token wird abgelehnt Token ist nach rund 2 Minuten abgelaufen Token unmittelbar vor dem Absenden lösen
Kosten steigen stärker als erwartet Unnötige Wiederholungen und Seitenaufrufe lösen zusätzliche Abfragen aus Nur kritische Schritte lösen und Wiederholungen je Quelle protokollieren

Häufige Fragen

Welche CAPTCHA-Typen treten auf Auktionsseiten auf?

Überwiegend reCAPTCHA v2 – bei Suche, Detailansichten und Gebotshistorie; beim Durchblättern von Kategorien gelegentlich Cloudflare Turnstile. CaptchaAI löst beide über dieselbe API.

Wie viele Auktionen kann ich gleichzeitig überwachen?

So viele, wie Ihr Plan an Threads bereitstellt. Jeder Thread verarbeitet eine laufende CAPTCHA-Abfrage und übernimmt nach der Lösung sofort die nächste. BASIC (15 $/Monat, 5 Threads) genügt für mehrere parallele Einträge; größere Beobachtungslisten profitieren von STANDARD oder ADVANCE.

Warum werden trotz gelöstem CAPTCHA keine Einträge geladen?

Meist stimmt die Reihenfolge nicht: Der Parser liest die Seite, bevor das Token angewendet wurde. Extrahieren Sie Einträge erst nach erfolgreicher Token-Übermittlung in derselben Sitzung.

Was kostet die Auktionsüberwachung?

Abgerechnet wird pro parallelem Thread, nicht pro Einzellösung – innerhalb eines Plans lösen Sie unbegrenzt viele CAPTCHAs. Für gelegentliches Monitoring reicht BASIC (15 $/Monat, 5 Threads); wer viele Einträge im Sekundentakt prüft, skaliert über STANDARD oder ADVANCE nach oben. Die Preise sind in US-Dollar und richten sich allein nach der Zahl paralleler Threads.

Brauche ich Residential-Proxys für die Überwachung?

Nicht zwingend, aber bei kurzen Intervallen sehr hilfreich. Rotierende Residential-Proxys verteilen Ihre Anfragen auf mehrere IPs und senken die CAPTCHA-Rate spürbar. Bei stündlichen Prüfungen genügt oft eine stabile Session ganz ohne Proxy.


Verwandte Leitfäden

Kommentare sind für diesen Artikel deaktiviert.