Use Cases

通过验证码处理监控航空公司票价

旅游和航空公司网站经常使用验证码来限制自动票价检查。检查多条路线票价的价格监控系统将遇到 reCAPTCHA 挑战、Cloudflare 插页式广告和速率限制机制。 CaptchaAI 处理验证码解决步骤,以便您的监控管道可以继续收集票价数据。

本指南展示了如何构建票价监控工作流程,以在价格检查期间检测和解决验证码问题。


监控工作流程

Schedule check → Request fare page → CAPTCHA detected?
                                         ↓ Yes
                                    Solve via CaptchaAI → Inject token → Retry request
                                         ↓ No
                                    Parse fare data → Store → Alert on price change

你需要什么

要求 细节
CaptchaAI API 密钥 验证码网站
Python 3.8+ 使用requests
代理人 旅游网站的自有服务器基础设施
pip install requests

CaptchaAI 求解器助手

import requests
import time

API_KEY = "YOUR_API_KEY"


def solve_recaptcha_v2(sitekey, pageurl):
    """Solve reCAPTCHA v2 and return the token."""
    submit = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": API_KEY, "method": "userrecaptcha",
        "googlekey": sitekey, "pageurl": pageurl, "json": 1
    }).json()

    if submit.get("status") != 1:
        raise RuntimeError(f"Submit error: {submit.get('request')}")

    task_id = submit["request"]
    time.sleep(20)

    for _ in range(30):
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY, "action": "get", "id": task_id, "json": 1
        }).json()
        if result.get("status") == 1:
            return result["request"]
        if result.get("request") != "CAPCHA_NOT_READY":
            raise RuntimeError(f"Solve error: {result['request']}")
        time.sleep(5)
    raise TimeoutError("Solve timed out")


def solve_turnstile(sitekey, pageurl):
    """Solve Cloudflare Turnstile and return the token."""
    submit = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": API_KEY, "method": "turnstile",
        "sitekey": sitekey, "pageurl": pageurl, "json": 1
    }).json()

    if submit.get("status") != 1:
        raise RuntimeError(f"Submit error: {submit.get('request')}")

    task_id = submit["request"]
    time.sleep(10)

    for _ in range(30):
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY, "action": "get", "id": task_id, "json": 1
        }).json()
        if result.get("status") == 1:
            return result["request"]
        if result.get("request") != "CAPCHA_NOT_READY":
            raise RuntimeError(f"Solve error: {result['request']}")
        time.sleep(5)
    raise TimeoutError("Solve timed out")

通过验证码处理进行票价监控

import json
from datetime import datetime


class FareMonitor:
    def __init__(self, proxy=None):
        self.session = requests.Session()
        if proxy:
            self.session.proxies = {
                "http": f"http://{proxy}",
                "https": f"http://{proxy}"
            }
        self.fare_history = {}

    def check_fare(self, route):
        """Check fare for a route, solving CAPTCHAs if needed."""
        url = route["url"]

        response = self.session.get(url)

        # Detect CAPTCHA in response
        if self._has_recaptcha(response.text):
            sitekey = self._extract_sitekey(response.text)
            token = solve_recaptcha_v2(sitekey, url)
            response = self.session.post(url, data={
                "g-recaptcha-response": token,
                **route.get("params", {})
            })

        elif self._has_turnstile(response.text):
            sitekey = self._extract_turnstile_key(response.text)
            token = solve_turnstile(sitekey, url)
            response = self.session.post(url, data={
                "cf-turnstile-response": token,
                **route.get("params", {})
            })

        return self._parse_fare(response.text, route)

    def _has_recaptcha(self, html):
        return "g-recaptcha" in html or "recaptcha/api" in html

    def _has_turnstile(self, html):
        return "cf-turnstile" in html or "turnstile" in html

    def _extract_sitekey(self, html):
        # Extract data-sitekey from reCAPTCHA div
        if 'data-sitekey="' in html:
            start = html.index('data-sitekey="') + 14
            end = html.index('"', start)
            return html[start:end]
        return None

    def _extract_turnstile_key(self, html):
        if 'data-sitekey="' in html:
            idx = html.index("cf-turnstile")
            start = html.index('data-sitekey="', idx) + 14
            end = html.index('"', start)
            return html[start:end]
        return None

    def _parse_fare(self, html, route):
        """Parse fare data from the response. Customize per target site."""
        # Placeholder — implement per site
        return {
            "route": route["name"],
            "timestamp": datetime.now().isoformat(),
            "raw_length": len(html)
        }

    def monitor_routes(self, routes):
        """Check all routes and report price changes."""
        results = []
        for route in routes:
            try:
                fare = self.check_fare(route)
                results.append(fare)
                print(f"[OK] {route['name']}: checked")
            except Exception as e:
                print(f"[ERROR] {route['name']}: {e}")
        return results


# Usage
routes = [
    {
        "name": "NYC-LAX",
        "url": "https://example-airline.com/search?from=JFK&to=LAX&date=2025-08-15",
        "params": {"adults": 1}
    },
    {
        "name": "SFO-ORD",
        "url": "https://example-airline.com/search?from=SFO&to=ORD&date=2025-08-20",
        "params": {"adults": 1}
    }
]

monitor = FareMonitor(proxy="user:pass@proxy.example.com:8080")
results = monitor.monitor_routes(routes)

for r in results:
    print(json.dumps(r, indent=2))

安排检查

使用 cron 或任务调度程序按计划运行监视器:

# Check fares every 6 hours
0 */6 * * * cd /path/to/project && python fare_monitor.py >> /var/log/fares.log 2>&1

票价捕获清单

  • Capture base fare, taxes, currency, and fare family in one payload so later comparisons stay usable.
  • 至少保留两个选择器后备,并在解析失败时存储原始 HTML 快照。
  • 记录每个解决事件的路线、出发日期、集合时间和验证码延迟。

故障排除

问题 原因 处理方式
频繁验证码 来自同一 IP 的请求过多 使用轮换自有服务器基础设施
陈旧的价格 缓存页面 添加缓存破坏标头或随机化请求参数
IP 被封锁 速率限制 增加检查之间的延迟,QA 测试会话
验证码解决失败 站点密钥提取错误 验证 sitekey 与页面上的验证码匹配

常问问题

我应该多久检查一次票价?

通常每 4 至 8 小时检查一次。更频繁的检查会增加验证码遇到次数和代理成本。

航空公司网站使用哪些验证码类型?

最常见的是 reCAPTCHA v2、Cloudflare Turnstile 或挑战页面,偶尔也会使用图像验证码。

我需要自有服务器基础设施吗?

是的。旅游网站主动阻止数据中心 IP。住宅或自有服务器基础设施的成功率明显更高。

我可以监控多家航空公司吗?

是的。为每个航空公司的响应格式自定义 _parse_fare 方法,并为每个站点添加路线。

如何处理 Cloudflare 验证流程 页面?

method=cloudflare_challenge 与代理一起使用。返回的 qa_session_cookie cookie 授予对站点的访问权限。请参阅Cloudflare 验证流程指南。


获取您的 CaptchaAI API 密钥

开始监控机票价格验证码网站。在价格跟踪工作流程中自动处理验证码。


相关指南

  • 如何使用API​​解决reCAPTCHA v2
  • 如何使用API​​解决Cloudflare Turnstile
  • 使用验证码处理进行财务数据抓取
  • 代理设置最佳实践
该文章已禁用评论。

相关文章

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

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

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

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

May 03, 2026
Tutorials 使用 CaptchaAI 构建客户端验证码管道
使用 Captcha AI 构建客户端验证码管道的分步教程,具有可直接重用的示例和清晰的 Captcha AI 工作流程。

使用 Captcha AI 构建客户端验证码管道的分步教程,具有可直接重用的示例和清晰的 Captcha AI 工作流程。

Apr 30, 2026