Use Cases

使用 Python 进行验证码抓取:实用指南

requests 抓取网页时,一旦命中 reCAPTCHA、Turnstile,脚本就卡在提交表单那一步——这不是 requests 的问题,它本来就不负责“看图识别”。解法很直接:把识别交给 CaptchaAI 的 API,大多数情况下不需要启动浏览器。本文用 requests + BeautifulSoup 演示完整流程,覆盖分页抓取、图片验证码与错误处理,附可运行代码。提醒一句:抓取前请确认你对目标数据拥有采集权限,遵守《网络安全法》《数据安全法》与目标站点的 robots 协议。

国内站点是 GeeTest,海外站点多是 reCAPTCHA/Turnstile

国内电商、票务类站点常用 GeeTest(极验)滑块验证码,海外站点以 reCAPTCHA v2/v3、Turnstile 为主。CaptchaAI 支持 reCAPTCHA v2/v3、Turnstile、GeeTest v3、图片/OCR 与九宫格验证码,CaptchaFox、Friendly Captcha、Lemin 是测试版;hCaptcha、FunCaptcha 暂不支持,GeeTest v4 官方状态是“即将支持”。

另外,reCAPTCHA 依赖 Google 托管脚本,部分国内网络下加载不稳定,脚本可能在加载阶段就超时——先确认目标资源能否访问,再排查代码逻辑。

环境准备

依赖 说明
Python 3.7+ 需自带 pip
requests 库 pip install requests
BeautifulSoup4 pip install beautifulsoup4
CaptchaAI API Key CaptchaAI 官网 注册获取

国内网络环境下 pip 安装慢是常事,可以加上清华 TUNA 镜像:pip install -i https://pypi.tuna.tsinghua.edu.cn/simple requests beautifulsoup4,两个包几秒钟就能装完。

封装一个 CaptchaAI 求解器类

把提交任务、轮询结果封装成一个类,后面直接复用,不用每次重写 in.php / res.php 的调用细节:

import requests
import time

class CaptchaSolver:
    def __init__(self, api_key):
        self.api_key = api_key
        self.base = "https://ocr.captchaai.com"

    def _submit(self, params):
        params["key"] = self.api_key
        resp = requests.get(f"{self.base}/in.php", params=params)
        if not resp.text.startswith("OK|"):
            raise Exception(f"Submit error: {resp.text}")
        return resp.text.split("|")[1]

    def _poll(self, task_id, timeout=300):
        deadline = time.time() + timeout
        while time.time() < deadline:
            time.sleep(5)
            resp = requests.get(f"{self.base}/res.php", params={
                "key": self.api_key,
                "action": "get",
                "id": task_id
            })
            if resp.text == "CAPCHA_NOT_READY":
                continue
            if resp.text.startswith("OK|"):
                return resp.text.split("|")[1]
            raise Exception(f"Solve error: {resp.text}")
        raise TimeoutError("Solve timed out")

    def solve_recaptcha_v2(self, site_key, page_url):
        task_id = self._submit({
            "method": "userrecaptcha",
            "googlekey": site_key,
            "pageurl": page_url
        })
        return self._poll(task_id)

    def solve_recaptcha_v3(self, site_key, page_url, action="verify"):
        task_id = self._submit({
            "method": "userrecaptcha",
            "googlekey": site_key,
            "pageurl": page_url,
            "version": "v3",
            "action": action
        })
        return self._poll(task_id)

    def solve_turnstile(self, site_key, page_url):
        task_id = self._submit({
            "method": "turnstile",
            "sitekey": site_key,
            "pageurl": page_url
        })
        return self._poll(task_id)

    def solve_image(self, image_base64):
        task_id = self._submit({
            "method": "base64",
            "body": image_base64
        })
        return self._poll(task_id)

覆盖 reCAPTCHA v2/v3、Turnstile 和图片验证码四种常见场景,_poll 负责轮询,超时抛出 TimeoutError

实战:抓取带 reCAPTCHA 验证码的表单

以一个带 reCAPTCHA v2 的搜索表单为例:加载页面、提取 sitekey、调用 CaptchaAI 识别、把 token 塞进表单字段再提交:

from bs4 import BeautifulSoup
import requests

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

# Step 1: Load the page
url = "https://example.com/search"
page = session.get(url)
soup = BeautifulSoup(page.text, "html.parser")

# Step 2: Extract the site key
recaptcha_div = soup.find("div", class_="g-recaptcha")
site_key = recaptcha_div["data-sitekey"]

# Step 3: Solve the CAPTCHA
token = solver.solve_recaptcha_v2(site_key, url)

# Step 4: Submit the form with the token
form_data = {
    "q": "search term",
    "g-recaptcha-response": token
}
result = session.post(url, data=form_data)

# Step 5: Parse the results
result_soup = BeautifulSoup(result.text, "html.parser")
items = result_soup.find_all("div", class_="result-item")
for item in items:
    print(item.text.strip())

关键在 g-recaptcha-response 字段名——写错的话,提交后会原样跳回验证码页面。

分页抓取:需要每页都重新识别一次验证码吗

有些站点每页翻页都挂验证码,有的只在首次挑战一次。稳妥做法是按页请求识别,宁可多调用几次 CaptchaAI,也不要省一次识别导致整批失败:

def scrape_all_pages(base_url, site_key, max_pages=10):
    solver = CaptchaSolver("YOUR_API_KEY")
    session = requests.Session()
    session.headers.update({
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
    })
    all_results = []

    for page_num in range(1, max_pages + 1):
        page_url = f"{base_url}?page={page_num}"

        # Solve CAPTCHA for each page if needed
        token = solver.solve_recaptcha_v2(site_key, page_url)

        resp = session.get(page_url, params={
            "g-recaptcha-response": token,
            "page": page_num
        })

        soup = BeautifulSoup(resp.text, "html.parser")
        items = soup.find_all("div", class_="item")

        if not items:
            break

        all_results.extend([item.text.strip() for item in items])
        print(f"Page {page_num}: {len(items)} items")

        time.sleep(2)  # Polite delay

    return all_results

time.sleep(2) 是故意留的礼貌延迟,减少并发压力,也顺带降低触发二次验证码的概率。

遇到图片验证码怎么办

不少老站点仍用最传统的图片文字验证码。处理思路更简单:下载图片、转 base64、调用 CaptchaAI 的图片识别接口:

import base64

def scrape_with_image_captcha(url):
    solver = CaptchaSolver("YOUR_API_KEY")
    session = requests.Session()

    page = session.get(url)
    soup = BeautifulSoup(page.text, "html.parser")

    # Find the CAPTCHA image
    captcha_img = soup.find("img", {"id": "captcha-image"})
    captcha_url = captcha_img["src"]

    # Download and encode the image
    img_resp = session.get(captcha_url)
    img_base64 = base64.b64encode(img_resp.content).decode()

    # Solve
    captcha_text = solver.solve_image(img_base64)

    # Submit
    form_data = {
        "captcha": captcha_text,
        "username": "user"
    }
    result = session.post(url, data=form_data)
    return result.text

图片验证码识别通常更快,因为不涉及行为分析,纯粹是 OCR 识别。

生产环境别忘了加重试逻辑

网络抖动、识别偶尔失败都正常,生产脚本必须有重试,否则一次瞬时错误就会中断整个任务:

def solve_with_retry(solver, site_key, page_url, max_retries=3):
    for attempt in range(max_retries):
        try:
            return solver.solve_recaptcha_v2(site_key, page_url)
        except Exception as e:
            if attempt == max_retries - 1:
                raise
            print(f"Attempt {attempt + 1} failed: {e}. Retrying...")
            time.sleep(2)

三次重试够用;规模大时还要考虑节流,参考验证码抓取的请求节流策略

常见报错排查

报错 原因 处理方式
ERROR_WRONG_USER_KEY API Key 无效 核对控制台密钥
ERROR_ZERO_BALANCE 余额不足 账户充值
提交后又跳回验证码页面 token 过期或字段名写错 拿到 token 立即提交;核对字段名
ConnectionError 网络问题 加指数退避重试逻辑
提交后结果为空 站点依赖 cookie/session requests.Session() 维持会话

常见问题

Python 验证码抓取一定要用 Selenium 吗?

不一定。表单能走标准 HTTP POST 时,requests + CaptchaAI 比 Selenium 更快更省资源;只有依赖 JavaScript 渲染的站点才需要 Selenium。

抓取时可以并发识别多个验证码吗?

可以,用 aiohttp 替换同步的 requests,参考aiohttp 异步验证码识别实践

国内站点用的 GeeTest 验证码,CaptchaAI 能处理吗?

能,但仅限 GeeTest v3;GeeTest v4 官方状态是“即将支持”,还不能用于生产环境。

多线程同时跑多个 CaptchaSolver 实例安全吗?

可以,但每个线程各自持有一个实例,requests.Session() 不跨线程共享;并发瓶颈通常在线程配额,而非代码本身。

抓取量很大,CaptchaAI 怎么计费?

按并发线程数计费,不按识别次数,同一线程内识别次数不设上限,套餐以官网价格页为准。

延伸阅读

该文章已禁用评论。