Use Cases

用于拍卖站点监控的验证码处理

拍卖平台使用 reCAPTCHA v2 保护列表数据和搜索功能。验证码最常出现在快速搜索查询、列表详细信息视图和出价历史记录查找过程中。以下是如何在拍卖网站上保持可靠的监控。

验证码在拍卖网站上触发的位置

行动 验证码类型 触发模式
搜索/browse 列表 reCAPTCHA v2 快速顺序搜索
查看房源详情 reCAPTCHA v2 同一IP的高流量
检查出价历史记录 reCAPTCHA v2 重复加载详细信息页面
分类浏览 Cloudflare Turnstile 类似机器人的导航速度
价格提醒页面 reCAPTCHA v2 频繁刷新

通过验证码解决拍卖监控

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"
)

价格警报系统 (JavaScript)

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();

监控策略

检查频率 使用案例 预期验证码率
每 30 秒 最后一刻出价 高——使用代理
每5分钟一班 主动拍卖跟踪 缓和
每15分钟一班 监视列表监控 低的
每小时 长期价格研究 最小

降低验证码频率

技术 影响
重用会话cookie 维护经过身份验证的状态
轮换自有服务器基础设施 跨 IP 分发请求
随机化请求间隔 避免周期性检测模式
使用经过身份验证的帐户 降低验证码触发阈值

故障排除

问题 原因 处理方式
针对每个请求的验证码 无会话持久性 重复使用requests.Session()
reCAPTCHA 令牌被拒绝 令牌已过期(2 分钟有效期) 提交前解决
列表页面显示 0 个结果 验证码静默过滤结果 检查隐藏的验证码元素
多次验证码后 IP 被阻止 超出速率限制 QA 测试会话,增加间隔

常问问题

我可以多快查看拍卖清单?

CaptchaAI 在 10-30 秒内解决 reCAPTCHA v2。对于时间敏感的拍卖,QA 预测试代币并QA 测试会话以最大程度地减少延迟。

拍卖网站会检测监控吗?

检测取决于请求模式,而不是验证码解决。使用切合实际的时间间隔,轮换用户代理,并避免在低流量时段当您的请求突出时进行抓取。

我可以实时监控现场拍卖吗?

在拍卖的最后几分钟,使用浏览器自动化和预先验证的会话来最大程度地减少验证码遭遇。 CaptchaAI 的速度可以处理仍然出现的任何验证码。

相关文章

  • 如何使用Api解决Recaptcha V2回调
  • Recaptcha V2 Turnstile同一站点处理
  • Recaptcha V2回调机制

下一步

可靠地监控拍卖 -”获取您的 CaptchaAI API 密钥并自动解决 reCAPTCHA v2 挑战。

该文章已禁用评论。

相关文章

Use Cases 通过验证码处理监控航空公司票价
通过验证码处理监控票价的实用指南,包含现实场景、工作流程建议以及使用 Captcha AI 的可行步骤。

通过验证码处理监控票价的实用指南,包含现实场景、工作流程建议以及使用 Captcha AI 的可行步骤。

Apr 18, 2026
Use Cases 使用验证码处理自动提交表单
使用验证码处理自动提交表单的实用指南,包含现实场景、工作流程建议以及使用 Captcha AI 的可行步骤。

使用验证码处理自动提交表单的实用指南,包含现实场景、工作流程建议以及使用 Captcha AI 的可行步骤。

Apr 20, 2026
Use Cases 持续集成测试中的验证码处理
持续集成测试中的验证码处理实用指南,包含现实场景、工作流程建议和使用 Captcha AI 的可操作步骤。

持续集成测试中的验证码处理实用指南,包含现实场景、工作流程建议和使用 Captcha AI 的可操作步骤。

May 03, 2026