Comparisons

reCAPTCHA v2 vs reCAPTCHA Invisible — Perbedaan dan Cara Solve

Kalau solver sudah mengembalikan token tetapi form target tetap menolak submit, halaman itu biasanya memakai reCAPTCHA v2 invisible, bukan checkbox. Di API CaptchaAI bedanya hanya satu parameter, invisible=1; di browser bedanya besar, karena tidak ada widget yang bisa diklik dan token harus Anda serahkan sendiri ke callback halaman.

Urutannya di bawah mengikuti alur kerja nyata: kenali markup, deteksi dari skrip, kirim task, pasang token.

Bentuk reCAPTCHA v2 checkbox dan invisible di HTML

Format sitekey keduanya identik, jadi hanya markup halaman yang jujur menunjukkan varian mana yang dipasang.

Varian checkbox

<!-- Standard checkbox widget -->
<div class="g-recaptcha"
     data-sitekey="6Le-wvkSAAAAAPBMRTvw..."
     data-callback="onSubmit">
</div>

<!-- Widget renders as: -->
<!-- [✓] I'm not a robot     reCAPTCHA logo -->

Pengguna melihat dan mengklik kotaknya sendiri. Bila Google curiga, kisi gambar muncul sebaris di bawah widget.

Varian invisible

<!-- Pattern 1: Invisible widget on a button -->
<button class="g-recaptcha"
        data-sitekey="6Le-wvkSAAAAAPBMRTvw..."
        data-callback="onSubmit"
        data-size="invisible">
  Submit
</button>

<!-- Pattern 2: Invisible div (programmatic trigger) -->
<div class="g-recaptcha"
     data-sitekey="6Le-wvkSAAAAAPBMRTvw..."
     data-size="invisible"
     data-callback="onSubmit">
</div>

<!-- Pattern 3: Programmatic render -->
<script>
  grecaptcha.render('submit-btn', {
    sitekey: '6Le-wvkSAAAAAPBMRTvw...',
    callback: onSubmit,
    size: 'invisible'
  });
</script>

Tidak ada elemen yang bisa diklik. reCAPTCHA aktif saat pengguna menyentuh elemen pemicu atau saat grecaptcha.execute() dijalankan kode halaman.

Deteksi varian reCAPTCHA langsung dari skrip

Untuk pipeline scraping tanpa browser, ambil HTML-nya lalu baca atributnya. Fungsi berikut mengembalikan tiga hal sekaligus: varian, sitekey, dan nama callback.

Versi Python

import requests
from bs4 import BeautifulSoup
import re

def detect_recaptcha_variant(url):
    resp = requests.get(url)
    soup = BeautifulSoup(resp.text, "html.parser")

    # Check for invisible indicators
    invisible_widget = soup.find(attrs={"data-size": "invisible", "class": "g-recaptcha"})
    if invisible_widget:
        return {
            "variant": "invisible",
            "sitekey": invisible_widget.get("data-sitekey"),
            "callback": invisible_widget.get("data-callback")
        }

    # Check for programmatic invisible in scripts
    for script in soup.find_all("script"):
        if script.string and "invisible" in str(script.string):
            key_match = re.search(r"sitekey['\"]?\s*[:=]\s*['\"]([^'\"]+)", script.string)
            if key_match:
                return {
                    "variant": "invisible-programmatic",
                    "sitekey": key_match.group(1),
                    "callback": "check grecaptcha.render() call"
                }

    # Check for standard checkbox
    checkbox_widget = soup.find(class_="g-recaptcha")
    if checkbox_widget:
        return {
            "variant": "checkbox",
            "sitekey": checkbox_widget.get("data-sitekey"),
            "callback": checkbox_widget.get("data-callback")
        }

    return None

result = detect_recaptcha_variant("https://staging.example.com/qa-login")
print(result)

Versi Node.js

Pola yang sama untuk worker berbasis Axios dan Cheerio:

const axios = require("axios");
const cheerio = require("cheerio");

async function detectRecaptchaVariant(url) {
  const { data } = await axios.get(url);
  const $ = cheerio.load(data);

  // Check for invisible
  const invisible = $(".g-recaptcha[data-size='invisible']");
  if (invisible.length) {
    return {
      variant: "invisible",
      sitekey: invisible.attr("data-sitekey"),
      callback: invisible.attr("data-callback"),
    };
  }

  // Check scripts for programmatic invisible
  const scripts = $("script")
    .map((_, el) => $(el).html())
    .get()
    .join("\n");
  if (scripts.includes("invisible")) {
    const keyMatch = scripts.match(/sitekey['"]?\s*[:=]\s*['"]([^'"]+)/);
    if (keyMatch) {
      return {
        variant: "invisible-programmatic",
        sitekey: keyMatch[1],
        callback: "check render call",
      };
    }
  }

  // Check for standard checkbox
  const checkbox = $(".g-recaptcha");
  if (checkbox.length) {
    return {
      variant: "checkbox",
      sitekey: checkbox.attr("data-sitekey"),
      callback: checkbox.attr("data-callback"),
    };
  }

  return null;
}

Cek cepat saat menelusuri halaman

Saat memeriksa halaman secara manual, satu baris di console browser sudah cukup:

const el = document.querySelector('[data-size="invisible"]');
console.log(el ? "Invisible reCAPTCHA" : "Checkbox reCAPTCHA");

Sebagian halaman merender widget lewat grecaptcha.render(), bukan lewat atribut HTML; blok pencocokan isi skrip di atas menutup celah itu.

Perbandingan singkat kedua varian

Fitur v2 checkbox v2 invisible
Widget terlihat Ya — checkbox "I'm not a robot" Tidak — tersembunyi dari pengguna
Pemicu Pengguna mengklik checkbox Klik tombol, submit form, atau load halaman
Tantangan gambar Muncul saat Google mencurigai bot Popup di pojok kanan bawah bila mencurigakan
Field token g-recaptcha-response g-recaptcha-response
Fungsi callback Opsional Hampir selalu wajib
Metode CaptchaAI method=userrecaptcha method=userrecaptcha + invisible=1
Masa berlaku token 120 detik 120 detik
Waktu penyelesaian 10–30 detik 10–25 detik

Masa berlaku 120 detik itu yang paling sering menjegal worker di AWS ap-southeast-1 (Singapura) atau ap-southeast-3 (Jakarta) saat halaman target lambat: token selesai di detik ke-20, form baru siap semenit kemudian, token telanjur kedaluwarsa. Ambil token sedekat mungkin dengan momen submit.

Kirim task ke CaptchaAI

Alurnya sama: kirim ke in.php → simpan task ID → polling res.php → pakai token.

Task untuk varian checkbox

import requests
import time

resp = requests.get("https://ocr.captchaai.com/in.php", params={
    "key": "YOUR_API_KEY",
    "method": "userrecaptcha",
    "googlekey": "6Le-wvkSAAAA...",
    "pageurl": "https://example.com/form"
})
task_id = resp.text.split("|")[1]

for _ in range(60):
    time.sleep(5)
    result = requests.get("https://ocr.captchaai.com/res.php", params={
        "key": "YOUR_API_KEY", "action": "get", "id": task_id
    })
    if result.text.startswith("OK|"):
        token = result.text.split("|")[1]
        break

Task untuk varian invisible

import requests
import time

resp = requests.get("https://ocr.captchaai.com/in.php", params={
    "key": "YOUR_API_KEY",
    "method": "userrecaptcha",
    "googlekey": "6Le-wvkSAAAA...",
    "pageurl": "https://example.com/form",
    "invisible": 1  # Only parameter difference
})
task_id = resp.text.split("|")[1]

for _ in range(60):
    time.sleep(5)
    result = requests.get("https://ocr.captchaai.com/res.php", params={
        "key": "YOUR_API_KEY", "action": "get", "id": task_id
    })
    if result.text.startswith("OK|"):
        token = result.text.split("|")[1]
        break

Satu-satunya baris yang berubah adalah "invisible": 1; tarif tidak berbeda antar-tipe karena paket CaptchaAI dihitung per thread, bukan per solve. BASIC ($15/bulan, 5 thread) cukup untuk satu worker QA, sedangkan STANDARD ($30/bulan, 15 thread) atau ADVANCE ($90/bulan, 50 thread) relevan saat banyak job paralel. Bagi pekerja lepas otomatisasi di Indonesia, biaya bulanan tetap juga lebih mudah ditagihkan ke klien.

Injeksi token: di sinilah keduanya benar-benar berpisah

Token-nya sama, cara memasangnya berbeda — dan di sinilah kegagalan paling sering muncul.

Checkbox: field tersembunyi biasanya sudah cukup

# Selenium — inject into hidden field
driver.execute_script(
    f'document.getElementById("g-recaptcha-response").value = "{token}";'
)

# If the page uses a callback, also call it
callback = driver.find_element("css selector", ".g-recaptcha").get_attribute("data-callback")
if callback:
    driver.execute_script(f'{callback}("{token}");')

Invisible: callback hampir selalu wajib dipanggil

# Selenium — inject AND call the callback
driver.execute_script(
    f'document.getElementById("g-recaptcha-response").value = "{token}";'
)

# CRITICAL: Invisible reCAPTCHA almost always requires calling the callback
callback_name = driver.find_element(
    "css selector", ".g-recaptcha[data-size='invisible']"
).get_attribute("data-callback")

driver.execute_script(f'{callback_name}("{token}");')

Versi Puppeteer untuk worker Node.js:

// Puppeteer — invisible callback injection
await page.evaluate((tok) => {
  // Set the hidden field
  document.getElementById("g-recaptcha-response").value = tok;

  // Find and call the callback function
  const widget = document.querySelector("[data-size='invisible']");
  const cbName = widget?.getAttribute("data-callback");
  if (cbName && typeof window[cbName] === "function") {
    window[cbName](tok);
  }
}, token);

Intinya: pada checkbox, klik pengguna sudah mendaftarkan callback sehingga mengisi field tersembunyi sering cukup. Pada invisible klik itu tidak pernah terjadi — panggil callback eksplisit, sebelum form auto-submit.

Satu solver untuk kedua varian

Karena bedanya hanya satu parameter, gabungkan deteksi dan solve. Kelas berikut menentukan varian, mengirim task, lalu mengembalikan token dan nama callback-nya.

import requests
import time
from bs4 import BeautifulSoup

class RecaptchaV2UniversalSolver:
    def __init__(self, api_key):
        self.api_key = api_key

    def detect_and_solve(self, page_url, page_html=None):
        if not page_html:
            page_html = requests.get(page_url).text

        soup = BeautifulSoup(page_html, "html.parser")

        # Detect variant
        invisible = soup.find(attrs={"data-size": "invisible", "class": "g-recaptcha"})
        widget = invisible or soup.find(class_="g-recaptcha")

        if not widget:
            raise Exception("No reCAPTCHA widget found")

        sitekey = widget.get("data-sitekey")
        is_invisible = invisible is not None
        callback = widget.get("data-callback")

        params = {
            "key": self.api_key,
            "method": "userrecaptcha",
            "googlekey": sitekey,
            "pageurl": page_url
        }
        if is_invisible:
            params["invisible"] = 1

        resp = requests.get("https://ocr.captchaai.com/in.php", params=params)
        if not resp.text.startswith("OK|"):
            raise Exception(f"Submit failed: {resp.text}")

        task_id = resp.text.split("|")[1]
        for _ in range(60):
            time.sleep(5)
            result = requests.get("https://ocr.captchaai.com/res.php", params={
                "key": self.api_key, "action": "get", "id": task_id
            })
            if result.text.startswith("OK|"):
                return {
                    "token": result.text.split("|")[1],
                    "variant": "invisible" if is_invisible else "checkbox",
                    "callback": callback,
                    "sitekey": sitekey
                }
            if result.text != "CAPCHA_NOT_READY":
                raise Exception(f"Solve error: {result.text}")

        raise Exception("Timed out")

# Usage
solver = RecaptchaV2UniversalSolver("YOUR_API_KEY")
result = solver.detect_and_solve("https://staging.example.com/qa-login")
print(f"Variant: {result['variant']}, Callback: {result['callback']}")

Pola ini awet untuk agensi price-monitoring: satu worker menangani puluhan halaman klien tanpa tahu di muka varian mana yang dipasang. Rincian versi checkbox ada di cara solve reCAPTCHA v2 lewat API.

Ketika token diterima solver tetapi ditolak halaman

Gejala Perbaikan di checkbox Perbaikan di invisible
Token diterima solver, form menolak Isi field g-recaptcha-response Isi field dan panggil fungsi data-callback
Widget tidak ditemukan skrip Cari class .g-recaptcha Cek data-size="invisible" atau panggilan render di skrip
Form terkirim tetapi gagal validasi Periksa apakah halaman mengharapkan callback Callback hampir pasti wajib — cari namanya lalu jalankan
Token ditolak karena kedaluwarsa Solve ulang; masa berlaku 120 detik Sama — ambil token tepat sebelum submit

Catatan kepatuhan lokal: otomatisasi dijalankan hanya pada halaman yang Anda miliki atau boleh proses, dan hindari data pribadi — UU Pelindungan Data Pribadi (UU 27/2022) mengatur datanya, bukan hanya caranya.

Pertanyaan yang sering muncul

Callback sudah dipanggil tetapi form tetap ditolak. Apa yang salah?

Biasanya urutannya. Di banyak halaman invisible, tombol submit ikut terpicu begitu callback selesai, jadi callback harus jalan sebelum skrip Anda menekan submit sendiri. Pastikan juga nama dari data-callback terdaftar sebagai fungsi di window.

Apakah hCaptcha atau FunCaptcha juga tercakup di alur ini?

Belum. hCaptcha dan FunCaptcha (Arkose Labs) tidak didukung, dan GeeTest v4 masih berstatus segera hadir. Yang tersedia: reCAPTCHA v2 dan v3, Cloudflare Turnstile dan Challenge, GeeTest v3, CAPTCHA gambar dan grid, plus CaptchaFox (beta), Friendly Captcha (beta), dan Lemin (beta).

Berapa thread yang saya butuhkan untuk solve invisible secara paralel?

Satu thread menampung satu solve yang sedang berjalan. Dengan waktu penyelesaian 10–25 detik, 5 thread pada BASIC ($15/bulan) berarti sekitar satu solve rampung tiap 2–5 detik. Naikkan paket saat antrean menumpuk, bukan berdasarkan volume bulanan — solve per thread tidak dibatasi.

Panduan terkait

Komentar dinonaktifkan untuk artikel ini.