Hướng Dẫn Thực Hành

Đồng thời có giới hạn tỷ lệ: Nhóm mã thông báo cho lệnh gọi API CAPTCHA

Đồng thời không được kiểm soát sẽ gửi yêu cầu nhanh nhất có thể. Điều đó dẫn đến ERROR_TOO_MUCH_REQUESTS, lãng phí số dư API và chi phí không thể đoán trước. Nhóm mã thông báo cho phép bạn đặt tốc độ chính xác - "không quá 20 lần gửi mỗi giây" - trong khi vẫn cho phép các đợt ngắn khi có đủ dung lượng.

Cách thức hoạt động của nhóm mã thông báo

[Bucket] capacity=20, refill=10/sec

Time 0:  ████████████████████  20 tokens available
         → 15 requests consume 15 tokens
Time 0:  █████                 5 tokens remain

Time 1s: ███████████████       15 tokens (5 + 10 refilled)
         → 15 requests consume 15 tokens
Time 1s: (empty)               0 tokens

Time 2s: ██████████            10 tokens (0 + 10 refilled)
         → Request waits if bucket is empty

Thuộc tính chính:

  • Dung lượng — kích thước cụm tối đa
  • Tốc độ nạp lại — số yêu cầu được duy trì mỗi giây
  • Yêu cầu chờ khi thùng trống (không từ chối, chỉ điều chỉnh)

Triển khai Python

Nhóm mã thông báo an toàn theo chủ đề

import time
import threading

class TokenBucket:
    def __init__(self, capacity, refill_rate):
        """
        Args:
            capacity: Maximum tokens (burst size)
            refill_rate: Tokens added per second
        """
        self.capacity = capacity
        self.refill_rate = refill_rate
        self.tokens = capacity
        self.last_refill = time.monotonic()
        self.lock = threading.Lock()

    def acquire(self, timeout=None):
        """Block until a token is available."""
        deadline = time.monotonic() + timeout if timeout else float("inf")

        while True:
            with self.lock:
                self._refill()
                if self.tokens >= 1:
                    self.tokens -= 1
                    return True

            # Check timeout
            if time.monotonic() >= deadline:
                return False

            # Wait before retrying (avoid busy loop)
            time.sleep(min(1.0 / self.refill_rate, 0.1))

    def _refill(self):
        now = time.monotonic()
        elapsed = now - self.last_refill
        new_tokens = elapsed * self.refill_rate
        self.tokens = min(self.capacity, self.tokens + new_tokens)
        self.last_refill = now

Bộ giải CAPTCHA có giới hạn tỷ lệ

import os
import requests
from concurrent.futures import ThreadPoolExecutor, as_completed

API_KEY = os.environ["CAPTCHAAI_API_KEY"]

# Allow 10 submissions/sec with burst of 20
rate_limiter = TokenBucket(capacity=20, refill_rate=10)

def solve_captcha_rate_limited(sitekey, pageurl):
    """Solve with rate limiting on submission."""
    # Wait for token before submitting
    rate_limiter.acquire()

    resp = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": API_KEY,
        "method": "userrecaptcha",
        "googlekey": sitekey,
        "pageurl": pageurl,
        "json": 1
    })
    data = resp.json()

    if data.get("status") != 1:
        raise RuntimeError(data.get("request"))

    captcha_id = data["request"]

    # Polling doesn't need rate limiting (separate concern)
    for _ in range(60):
        time.sleep(5)
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY, "action": "get", "id": captcha_id, "json": 1
        }).json()

        if result.get("status") == 1:
            return result["request"]
        if result.get("request") != "CAPCHA_NOT_READY":
            raise RuntimeError(result.get("request"))

    raise TimeoutError("Solve timeout")

# Run 100 tasks through rate limiter
tasks = [
    {"sitekey": "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
     "pageurl": f"https://example.com/p/{i}"}
    for i in range(100)
]

with ThreadPoolExecutor(max_workers=30) as executor:
    futures = {
        executor.submit(
            solve_captcha_rate_limited, t["sitekey"], t["pageurl"]
        ): t for t in tasks
    }

    for future in as_completed(futures):
        task = futures[future]
        try:
            solution = future.result()
            print(f"[OK] {task['pageurl']}")
        except Exception as e:
            print(f"[ERR] {task['pageurl']}: {e}")

Triển khai JavaScript

Nhóm mã thông báo không đồng bộ

class TokenBucket {
  constructor(capacity, refillRate) {
    this.capacity = capacity;
    this.refillRate = refillRate; // tokens per second
    this.tokens = capacity;
    this.lastRefill = Date.now();
    this.waitQueue = [];
  }

  _refill() {
    const now = Date.now();
    const elapsed = (now - this.lastRefill) / 1000;
    this.tokens = Math.min(this.capacity, this.tokens + elapsed * this.refillRate);
    this.lastRefill = now;
  }

  async acquire() {
    this._refill();

    if (this.tokens >= 1) {
      this.tokens -= 1;
      return;
    }

    // Wait until a token is available
    const waitTime = ((1 - this.tokens) / this.refillRate) * 1000;
    await new Promise((resolve) => setTimeout(resolve, waitTime));

    this._refill();
    this.tokens -= 1;
  }
}

Bộ giải hàng loạt có giới hạn tỷ lệ

const axios = require("axios");

const API_KEY = process.env.CAPTCHAAI_API_KEY;
const rateLimiter = new TokenBucket(20, 10); // 20 burst, 10/sec sustained

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function solveCaptchaLimited(sitekey, pageurl) {
  // Wait for rate limit token
  await rateLimiter.acquire();

  const submitResp = await axios.post(
    "https://ocr.captchaai.com/in.php",
    null,
    {
      params: {
        key: API_KEY,
        method: "userrecaptcha",
        googlekey: sitekey,
        pageurl: pageurl,
        json: 1,
      },
    }
  );

  if (submitResp.data.status !== 1) {
    throw new Error(submitResp.data.request);
  }

  const captchaId = submitResp.data.request;

  for (let i = 0; i < 60; i++) {
    await sleep(5000);
    const result = await axios.get("https://ocr.captchaai.com/res.php", {
      params: { key: API_KEY, action: "get", id: captchaId, json: 1 },
    });

    if (result.data.status === 1) return result.data.request;
    if (result.data.request !== "CAPCHA_NOT_READY") {
      throw new Error(result.data.request);
    }
  }

  throw new Error("TIMEOUT");
}

// Solve 100 tasks — rate limiter ensures max 10 submissions/sec
async function batchSolve(tasks) {
  const results = await Promise.allSettled(
    tasks.map((t) => solveCaptchaLimited(t.sitekey, t.pageurl))
  );

  const solved = results.filter((r) => r.status === "fulfilled").length;
  const failed = results.filter((r) => r.status === "rejected").length;
  console.log(`Solved: ${solved}, Failed: ${failed}`);
}

Chọn tham số

Khối lượng công việc Công suất (nổ) Tỷ lệ nạp lại (duy trì)
Cạo nhẹ 5 2/sec
Tự động hóa tiêu chuẩn 20 10/sec
Đường ống khối lượng lớn 50 30/sec
Thông lượng tối đa 100 50/sec

Quy tắc chung:

  • Đặt công suất thành 2× tốc độ nạp lại (cho phép bùng nổ trong 2 giây)
  • Bắt đầu thận trọng, tăng lên trong khi theo dõi tỷ lệ lỗi
  • Chỉ gửi giới hạn tỷ lệ - bỏ phiếu rất nhẹ và tự giới hạn

Nhóm mã thông báo so với các thuật toán khác

Thuật toán Hành vi Tốt nhất cho
Nhóm mã thông báo Tốc độ mượt mà với phụ cấp bùng nổ Lệnh gọi API CAPTCHA
Xô bị rò rỉ Tốc độ đầu ra cố định, không nổ Yêu cầu tỷ lệ nghiêm ngặt
Cửa sổ cố định Đếm trên mỗi cửa sổ thời gian, bùng nổ cạnh quầy đơn giản
Cửa sổ trượt Đếm trong thời gian lăn Thực thi tỷ giá chính xác

Nhóm mã thông báo là mặc định tốt nhất — nó cho phép bùng nổ tự nhiên (trình quét tìm thấy 20 CAPTCHA cùng một lúc) trong khi thực thi tốc độ duy trì.

Khắc phục sự cố

Vấn đề Nguyên nhân Cách xử lý
Yêu cầu vẫn đang bị hạn chế Bộ giới hạn tốc độ được đặt cao hơn mức API cho phép Tỷ lệ nạp lại thấp hơn để phù hợp với giới hạn của CaptchaAI
Độ trễ cao theo yêu cầu Token đã cạn, đang chờ nạp lại Tăng công suất cho các kịch bản bùng nổ
Trí nhớ ngày càng tăng Hàng chờ tích lũy Đặt kích thước hàng đợi tối đa; từ chối yêu cầu vượt quá
Giới hạn tốc độ không được chia sẻ giữa các quy trình Chỉ trong bộ nhớ Sử dụng nhóm mã thông báo dựa trên Redis để giới hạn tốc độ phân phối

Câu hỏi thường gặp

Tôi nên giới hạn tỷ lệ gửi bài, bỏ phiếu hay cả hai?

Chỉ gửi giới hạn tỷ lệ. Yêu cầu thăm dò rất nhẹ và tự điều chỉnh thông qua time.sleep(5). Bỏ phiếu quá giới hạn làm tăng giải quyết độ trễ mà không có lợi.

Làm cách nào để xử lý ERROR_TOO_MUCH_REQUESTS mặc dù bị giới hạn tốc độ?

Giới hạn tỷ lệ của bạn được đặt quá cao. Giảm tỷ lệ nạp lại. Đồng thời kiểm tra xem nhiều quy trình có chia sẻ cùng một khóa API hay không - tỷ lệ tổng hợp trên tất cả các quy trình.

Tôi có thể sử dụng bộ giới hạn tốc độ cho mỗi loại CAPTCHA không?

Có - tạo các nhóm mã thông báo riêng biệt cho các loại CAPTCHA khác nhau. Điều này ngăn không cho các tác vụ reCAPTCHA v2 có khối lượng lớn bỏ đói các lần gửi Turnstile.

bài viết liên quan

Các bước tiếp theo

Xây dựng giải pháp CAPTCHA được kiểm soát theo tỷ lệ —lấy khóa API CaptchaAI của bạnvà thực hiện tỷ lệ yêu cầu bền vững.

Hướng dẫn liên quan:

Os comentários estão desativados para este artigo.