应用场景

药房价格比较的验证码处理

构建药房价格比较工具意味着从多个药房门户、药品定价数据库和保险处方集收集定价数据。其中许多来源都使用验证码保护其定价页面 - 特别是搜索表单和结果页面上的 reCAPTCHA v2。以下是处理它们的方法。

验证码出现在药房门户中的位置

页面类型 常见验证码 扳机
药品检索表 reCAPTCHA v2 每个搜索查询
价格结果页面 Cloudflare Turnstile 疑似自动访问
药房定位器 reCAPTCHA v2 基于位置的查询
优惠券查询 图片验证码 显示折扣代码之前
保险处方集 reCAPTCHA v2 登录/search门

基本工作流程

import requests
import time

session = requests.Session()
session.headers.update({
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
})

def solve_recaptcha(site_key, page_url):
    resp = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": "YOUR_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": "YOUR_API_KEY",
            "action": "get",
            "id": task_id,
            "json": 1
        })
        data = result.json()
        if data["status"] == 1:
            return data["request"]
    raise TimeoutError("Solve timed out")

def search_drug_prices(drug_name, zipcode):
    search_url = "https://pharmacy.example.com/search"

    # Load the search page to get the site key
    page = session.get(search_url)

    # Extract reCAPTCHA site key
    import re
    match = re.search(r'data-sitekey="([^"]+)"', page.text)
    site_key = match.group(1)

    # Solve the CAPTCHA
    token = solve_recaptcha(site_key, search_url)

    # Submit the search with token
    results = session.post(search_url, data={
        "drug": drug_name,
        "zip": zipcode,
        "g-recaptcha-response": token
    })

    return parse_prices(results.text)

多药房比较

比较多个来源的价格:

PHARMACY_SOURCES = [
    {
        "name": "PharmacyA",
        "url": "https://pharmacya.example.com/pricing",
        "captcha_type": "recaptcha_v2"
    },
    {
        "name": "PharmacyB",
        "url": "https://pharmacyb.example.com/drugs",
        "captcha_type": "turnstile"
    },
    {
        "name": "PharmacyC",
        "url": "https://pharmacyc.example.com/search",
        "captcha_type": "image"
    }
]

def compare_drug_price(drug_name, zipcode):
    results = []

    for source in PHARMACY_SOURCES:
        try:
            prices = fetch_prices(
                source["url"],
                source["captcha_type"],
                drug_name,
                zipcode
            )
            results.append({
                "source": source["name"],
                "prices": prices,
                "status": "success"
            })
        except Exception as e:
            results.append({
                "source": source["name"],
                "error": str(e),
                "status": "failed"
            })

    return sorted(results, key=lambda r: r.get("prices", {}).get("lowest", float("inf")))

处理不同的验证码类型

药房门户使用各种验证码提供商:

def fetch_prices(url, captcha_type, drug_name, zipcode):
    page = session.get(url)

    if captcha_type == "recaptcha_v2":
        import re
        match = re.search(r'data-sitekey="([^"]+)"', page.text)
        token = solve_recaptcha(match.group(1), url)
        field_name = "g-recaptcha-response"

    elif captcha_type == "turnstile":
        import re
        match = re.search(r'data-sitekey="(0x[^"]+)"', page.text)
        token = solve_turnstile(match.group(1), url)
        field_name = "cf-turnstile-response"

    elif captcha_type == "image":
        img_data = extract_captcha_image(page.text)
        token = solve_image_captcha(img_data)
        field_name = "captcha"

    return session.post(url, data={
        "drug": drug_name,
        "zip": zipcode,
        field_name: token
    })

重复搜索的会话管理

药品价格比较需要多次搜索。管理会话以最大程度地减少验证码遭遇:

class PharmacySession:
    def __init__(self, base_url):
        self.session = requests.Session()
        self.base_url = base_url
        self.searches_since_captcha = 0

    def search(self, drug_name, zipcode):
        result = self.session.post(f"{self.base_url}/search", data={
            "drug": drug_name,
            "zip": zipcode
        })

        if self.is_captcha_page(result.text):
            token = self.solve_page_captcha(result.text)
            result = self.session.post(f"{self.base_url}/search", data={
                "drug": drug_name,
                "zip": zipcode,
                "g-recaptcha-response": token
            })
            self.searches_since_captcha = 0
        else:
            self.searches_since_captcha += 1

        return result

    def is_captcha_page(self, html):
        return "g-recaptcha" in html or "cf-turnstile" in html

    def solve_page_captcha(self, html):
        import re
        match = re.search(r'data-sitekey="([^"]+)"', html)
        return solve_recaptcha(match.group(1), self.base_url)

JavaScript 实现

async function comparePharmacyPrices(drugName, zipCode, sources) {
  const results = await Promise.all(
    sources.map(async (source) => {
      try {
        const price = await fetchDrugPrice(source, drugName, zipCode);
        return { source: source.name, price, status: 'success' };
      } catch (error) {
        return { source: source.name, error: error.message, status: 'failed' };
      }
    })
  );

  return results
    .filter(r => r.status === 'success')
    .sort((a, b) => a.price - b.price);
}

async function fetchDrugPrice(source, drugName, zipCode) {
  // Solve CAPTCHA for this source
  const token = await solveCaptcha(source.siteKey, source.url);

  const response = await fetch(source.url, {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      drug: drugName,
      zip: zipCode,
      'g-recaptcha-response': token
    })
  });

  return parsePrice(await response.text());
}

速率限制注意事项

药房门户密切监控访问模式:

战略 执行
空间请求 搜索间隔 5 至 10 秒
轮换会议 每 20-30 个查询就有一个新会话
改变搜索模式 不要重复搜索同一种药物
使用自有服务器基础设施 数据中心 IP 触发更多验证码

故障排除

问题 原因 处理方式
每次搜索时都会显示验证码 会话不保留 cookie 使用requests.Session()并维护cookie
验证码后未加载价格 缺少 CSRF 令牌 提取并包含隐藏的表单字段
多次搜索后网站被屏蔽 速率限制 在请求之间添加延迟
价格与浏览器不同 缺少会话 cookie 或标头 完全匹配浏览器标头

常问问题

我可以同时比较多少个药房来源?

CaptchaAI 处理并行求解。您可以同时解决多个药房门户的验证码。实际限制取决于您的 CaptchaAI 余额和每个门户的速率限制。

药房门户是否经常更改其验证码类型?

一些门户网站定期更新其验证码实施。通过检查提取是否仍能找到站点密钥来监视更改。 CaptchaAI 支持所有主要的验证码类型,因此切换提供商不需要改变您的解决方法。

品牌药品定价门户和仿制药定价门户之间有区别吗?

验证码处理类似。仿制药数据库可能使用更简单的图像验证码,而品牌药房门户网站倾向于使用 reCAPTCHA v2 或 Turnstile。

相关文章

下一步

构建您的药房价格比较工具 –”获取您的 CaptchaAI API 密钥并处理所有药房来源的验证码。

该文章已禁用评论。