Tutorial

Logging Terstruktur untuk Operasi CAPTCHA

Ketika solve CAPTCHA gagal pada jam 3 pagi, log teks biasa seperti "Error solving captcha" tidak membantu. Log JSON terstruktur dengan ID tugas, jenis captcha, waktu solve, dan kode kesalahan memungkinkan Anda memfilter, mencari, dan memperingatkan apa yang salah.


Mengapa logging terstruktur

Teks biasa JSON terstruktur
Captcha solved in 12.3s {"event":"captcha_solved","task_id":"abc123","type":"recaptcha_v2","solve_time_ms":12300}
Sulit untuk diurai Dapat dibaca mesin
Pencarian khusus Grep Filter berdasarkan bidang apa pun
Tidak ada korelasi Tautan ID tugas mengirimkan -> jajak pendapat -> menyuntikkan

Python: structlog

import structlog
import time

structlog.configure(
    processors=[
        structlog.processors.TimeStamper(fmt="iso"),
        structlog.processors.add_log_level,
        structlog.processors.JSONRenderer(),
    ],
    logger_factory=structlog.PrintLoggerFactory(),
)

log = structlog.get_logger()

Mencatat siklus hidup solve

import requests

API_KEY = "YOUR_API_KEY"


def solve_captcha(captcha_type, sitekey, page_url, proxy=None):
    solve_log = log.bind(
        captcha_type=captcha_type,
        site_url=page_url,
        sitekey=sitekey[:12] + "...",
    )

    # Submit
    start = time.time()
    solve_log.info("captcha_submit_start")

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

    if resp["status"] != 1:
        solve_log.error("captcha_submit_failed", error=resp["request"])
        return None

    task_id = resp["request"]
    submit_ms = int((time.time() - start) * 1000)
    solve_log = solve_log.bind(task_id=task_id)
    solve_log.info("captcha_submitted", submit_ms=submit_ms)

    # Poll
    for attempt in range(24):
        time.sleep(5)
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY, "action": "get", "id": task_id, "json": "1"
        }).json()

        if result["status"] == 1:
            solve_ms = int((time.time() - start) * 1000)
            solve_log.info(
                "captcha_solved",
                solve_time_ms=solve_ms,
                poll_attempts=attempt + 1,
                token_length=len(result["request"]),
            )
            return result["request"]

        if result["request"] != "CAPCHA_NOT_READY":
            solve_log.error(
                "captcha_solve_failed",
                error=result["request"],
                poll_attempts=attempt + 1,
            )
            return None

    solve_log.warning("captcha_solve_timeout", poll_attempts=24)
    return None

Keluaran:

{"event":"captcha_submit_start","captcha_type":"recaptcha_v2","site_url":"https://example.com","sitekey":"6Le-wvkSAAAA...","timestamp":"2025-07-15T10:30:00Z","level":"info"}
{"event":"captcha_submitted","task_id":"71845302","submit_ms":245,"timestamp":"2025-07-15T10:30:00Z","level":"info"}
{"event":"captcha_solved","task_id":"71845302","solve_time_ms":18230,"poll_attempts":4,"token_length":580,"timestamp":"2025-07-15T10:30:18Z","level":"info"}

Node.js: pino

const pino = require('pino');

const log = pino({
  level: 'info',
  timestamp: pino.stdTimeFunctions.isoTime,
});

Mencatat siklus hidup solve

const axios = require('axios');

const API_KEY = 'YOUR_API_KEY';

async function solveCaptcha(captchaType, sitekey, pageUrl) {
  const taskLog = log.child({
    captchaType,
    siteUrl: pageUrl,
    sitekey: sitekey.substring(0, 12) + '...',
  });

  const start = Date.now();
  taskLog.info('captcha_submit_start');

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

  if (submit.data.status !== 1) {
    taskLog.error({ error: submit.data.request }, 'captcha_submit_failed');
    return null;
  }

  const taskId = submit.data.request;
  const boundLog = taskLog.child({ taskId });
  boundLog.info({ submitMs: Date.now() - start }, 'captcha_submitted');

  for (let attempt = 1; attempt <= 24; attempt++) {
    await new Promise(r => setTimeout(r, 5000));
    const poll = await axios.get('https://ocr.captchaai.com/res.php', {
      params: { key: API_KEY, action: 'get', id: taskId, json: 1 },
    });

    if (poll.data.status === 1) {
      boundLog.info({
        solveTimeMs: Date.now() - start,
        pollAttempts: attempt,
        tokenLength: poll.data.request.length,
      }, 'captcha_solved');
      return poll.data.request;
    }

    if (poll.data.request !== 'CAPCHA_NOT_READY') {
      boundLog.error({ error: poll.data.request, pollAttempts: attempt }, 'captcha_solve_failed');
      return null;
    }
  }

  boundLog.warn({ pollAttempts: 24 }, 'captcha_solve_timeout');
  return null;
}

Referensi bidang log

Bidang Ketik Deskripsi
event tali Nama acara: captcha_submitted, captcha_solved, dll.
task_id tali ID tugas CaptchaAI untuk korelasi
captcha_type tali recaptcha_v2, turnstile, image, dll.
site_url tali URL halaman target
solve_time_ms bilangan bulat Total waktu dari pengiriman hingga solve
poll_attempts bilangan bulat Jumlah request jajak pendapat yang dibuat
error tali Kode kesalahan dari CaptchaAI
token_length bilangan bulat Panjang token yang dikembalikan

Memfilter dan memperingatkan

Temukan semua kegagalan dalam satu jam terakhir

# With jq
cat captcha.log | jq 'select(.level == "error" and .event == "captcha_solve_failed")'

Peringatan tentang tingkat kegagalan yang tinggi

# Count errors vs successes in a rolling window
from collections import deque

class ErrorRateMonitor:
    def __init__(self, window_size=100, threshold=0.2):
        self.results = deque(maxlen=window_size)
        self.threshold = threshold

    def record(self, success):
        self.results.append(success)
        if len(self.results) >= 50:
            error_rate = 1 - sum(self.results) / len(self.results)
            if error_rate > self.threshold:
                log.warning(
                    "captcha_error_rate_high",
                    error_rate=round(error_rate, 3),
                    window=len(self.results),
                )

Pemecahan Masalah

Masalah Penyebab Solusi
Log terlalu bertele-tele Mencatat setiap upaya jajak pendapat Log hanya mengirimkan, menyelesaikan, dan peristiwa yang gagal
Tidak dapat menghubungkan peristiwa ID tugas tidak ada Ikat task_id lebih awal dengan log.bind() atau log.child()
Log tidak dapat dicari Format teks biasa Beralih ke JSON dengan structlog atau pino
Data sensitif dalam log Mencatat kunci API lengkap Jangan pernah mencatat kunci API; potong sitekey

Pertanyaan Umum

Haruskah saya mencatat token CAPTCHA?

Catat panjang token, bukan token lengkap. Token dapat berisi 500+ karakter dan menambahkan volume log yang tidak diperlukan.

Level log mana untuk CAPCHA_NOT_READY?

Jangan mencatatnya sama sekali – hal ini diperkirakan terjadi pada saat pemungutan suara. Hanya catat hasil akhirnya.


Bangun alur kerja CAPTCHA yang dapat diamati dengan CaptchaAI

Dapatkan kunci API Anda dicaptchaai.com.


Panduan terkait

Komentar dinonaktifkan untuk artikel ini.