Sebagian besar integrasi CaptchaAI tidak pernah menyentuh URL anchor atau bframe — cukup kirim sitekey dan pageurl, token pun kembali. Kedua URL iframe ini baru relevan saat token ditolak berulang kali padahal parameter solve terlihat benar, atau saat sitekey tidak muncul di HTML awal karena baru dimuat lewat JavaScript. Panduan ini membedah arsitektur iframe reCAPTCHA, format parameternya, tiga cara mengekstraknya (Python, Node.js, Selenium), dan kapan debugging ini benar-benar dibutuhkan.
Arsitektur iframe reCAPTCHA: anchor vs bframe
Dua iframe ini punya peran yang berbeda dan tidak selalu dimuat bersamaan:
| Iframe | Kapan dimuat | Isi |
|---|---|---|
| Anchor | Selalu, begitu halaman selesai render | Widget checkbox "I'm not a robot" + analisis risiko awal |
| Bframe | Hanya saat checkbox diklik dan tantangan dipicu | Grid tantangan gambar |
Target page (staging.example.com/qa-login)
└── <iframe src="https://www.google.com/recaptcha/api2/anchor?...">
│ ← Anchor iframe: "I'm not a robot" checkbox
│
└── <iframe src="https://www.google.com/recaptcha/api2/bframe?...">
← Bframe iframe: Image challenge grid (loads when clicked)
Iframe anchor: checkbox dan analisis risiko awal
https://www.google.com/recaptcha/api2/anchor?
ar=1
&k=6LcR_RsTAAAAAN_r0GEkGBfq3L7KmU5JbPHJtwNp ← site key
&co=aHR0cHM6Ly9leGFtcGxlLmNvbTo0NDM. ← encoded origin
&hl=en ← language
&v=jF2Zb_rr_5sv8dMHoGIn-XxY ← reCAPTCHA version
&size=normal ← widget size
&cb=89fu2pf0swif ← callback ID
Iframe bframe: tantangan gambar
https://www.google.com/recaptcha/api2/bframe?
hl=en
&v=jF2Zb_rr_5sv8dMHoGIn-XxY
&k=6LcR_RsTAAAAAN_r0GEkGBfq3L7KmU5JbPHJtwNp
Parameter di URL anchor reCAPTCHA
| Parameter | Nama | Keterangan |
|---|---|---|
k |
Sitekey | Sitekey reCAPTCHA situs terkait |
co |
Origin terenkode | Origin yang dikodekan Base64 (protokol + domain + port) |
v |
Versi | Hash versi bundel JavaScript reCAPTCHA |
hl |
Bahasa | Kode bahasa tantangan |
size |
Ukuran | normal, compact, atau invisible |
cb |
Callback | ID unik fungsi callback |
theme |
Tema | light atau dark |
ar |
Rasio aspek | Penanda tampilan rasio aspek |
Dari delapan parameter di atas, tiga yang paling sering dipakai untuk debugging adalah:
k— memastikan sitekey yang dikirim ke solver benar-benar cocok dengan yang dimuat halamanco— mengungkap domain yang menurut reCAPTCHA sedang dijalankanv— menandai versi bundel reCAPTCHA saat masalah muncul, berguna untuk melacak apakah update Google yang jadi penyebab
Cara membaca parameter co
Parameter co menyimpan origin situs dalam bentuk base64:
import base64
co_value = "aHR0cHM6Ly9leGFtcGxlLmNvbTo0NDM."
# Remove trailing period (padding artifact)
decoded = base64.b64decode(co_value.rstrip(".") + "==").decode()
print(decoded) # "https://example.com:443"
Hasil decode-nya menunjukkan domain persis tempat reCAPTCHA dikonfigurasi — inilah yang nanti dibandingkan dengan pageurl yang Anda kirim ke solver.
Tips debugging: simpan hasil decode
codi log Anda. Kalau origin-nya konsisten dari hari ke hari, domain mismatch bukan penyebab kegagalan solve — cari masalah di tempat lain.
Tiga cara ekstrak URL anchor dan bframe reCAPTCHA
Pilih berdasarkan cara halaman target me-render reCAPTCHA-nya:
- Python (requests + BeautifulSoup) — reCAPTCHA sudah ada di HTML awal (server-rendered), tanpa perlu browser.
- Node.js (axios + cheerio) — sama seperti Python tapi untuk stack yang sudah berbasis JavaScript.
- Selenium — reCAPTCHA baru muncul setelah JavaScript halaman selesai jalan (client-side rendering).
Ekstraksi Python dari sumber HTML statis
import requests
from bs4 import BeautifulSoup
from urllib.parse import urlparse, parse_qs
import re
import base64
def extract_recaptcha_iframes(url):
"""Extract reCAPTCHA anchor and bframe iframe URLs and parameters."""
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/120.0.0.0 Safari/537.36",
}
response = requests.get(url, headers=headers, timeout=15)
soup = BeautifulSoup(response.text, "html.parser")
result = {
"anchor_url": None,
"bframe_url": None,
"site_key": None,
"origin": None,
"version": None,
"language": None,
}
# Find anchor iframe
anchor_iframe = soup.find("iframe", src=re.compile(r"recaptcha.*anchor"))
if anchor_iframe:
anchor_url = anchor_iframe.get("src", "")
result["anchor_url"] = anchor_url
# Parse parameters
parsed = urlparse(anchor_url)
params = parse_qs(parsed.query)
result["site_key"] = params.get("k", [None])[0]
result["version"] = params.get("v", [None])[0]
result["language"] = params.get("hl", [None])[0]
# Decode origin
co = params.get("co", [None])[0]
if co:
try:
padded = co.rstrip(".") + "=="
result["origin"] = base64.b64decode(padded).decode()
except Exception:
result["origin"] = co
# Find bframe iframe (may not be in source — loaded dynamically)
bframe_iframe = soup.find("iframe", src=re.compile(r"recaptcha.*bframe"))
if bframe_iframe:
result["bframe_url"] = bframe_iframe.get("src", "")
# Construct bframe URL from anchor parameters if not found
if not result["bframe_url"] and result["site_key"] and result["version"]:
result["bframe_url"] = (
f"https://www.google.com/recaptcha/api2/bframe?"
f"hl={result['language'] or 'en'}"
f"&v={result['version']}"
f"&k={result['site_key']}"
)
return result
iframes = extract_recaptcha_iframes("https://staging.example.com/qa-login")
print(f"Site key: {iframes['site_key']}")
print(f"Origin: {iframes['origin']}")
print(f"Anchor URL: {iframes['anchor_url']}")
Ekstraksi dengan Node.js
const axios = require("axios");
const cheerio = require("cheerio");
const { URL } = require("url");
async function extractRecaptchaIframes(pageUrl) {
const { data: html } = await axios.get(pageUrl, {
headers: {
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) " +
"AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36",
},
timeout: 15000,
});
const $ = cheerio.load(html);
const result = {
anchorUrl: null,
bframeUrl: null,
siteKey: null,
origin: null,
version: null,
};
// Find anchor iframe
const anchorIframe = $("iframe[src*='recaptcha'][src*='anchor']");
if (anchorIframe.length) {
const src = anchorIframe.attr("src");
result.anchorUrl = src;
const url = new URL(src);
result.siteKey = url.searchParams.get("k");
result.version = url.searchParams.get("v");
// Decode origin
const co = url.searchParams.get("co");
if (co) {
try {
result.origin = Buffer.from(
co.replace(/\.$/, ""), "base64"
).toString();
} catch {}
}
}
// Construct bframe URL
if (result.siteKey && result.version) {
result.bframeUrl =
`https://www.google.com/recaptcha/api2/bframe?` +
`hl=en&v=${result.version}&k=${result.siteKey}`;
}
return result;
}
extractRecaptchaIframes("https://staging.example.com/qa-login").then(console.log);
Ekstraksi dengan Selenium (untuk halaman yang dimuat dinamis)
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
def extract_iframes_selenium(url):
"""Extract reCAPTCHA iframe URLs from a dynamically loaded page."""
driver = webdriver.Chrome()
driver.get(url)
time.sleep(3) # Wait for reCAPTCHA to load
result = {"anchor_url": None, "bframe_url": None}
# Find all iframes
iframes = driver.find_elements(By.TAG_NAME, "iframe")
for iframe in iframes:
src = iframe.get_attribute("src") or ""
if "recaptcha" in src and "anchor" in src:
result["anchor_url"] = src
elif "recaptcha" in src and "bframe" in src:
result["bframe_url"] = src
driver.quit()
return result
Kapan ekstraksi anchor/bframe reCAPTCHA benar-benar diperlukan
Skenario umum di tim scraping dan price-monitoring Indonesia: dashboard yang di-deploy di GCP asia-southeast2 (Jakarta) atau AWS ap-southeast-1 (Singapura) tiba-tiba gagal solve reCAPTCHA, padahal sitekey dan pageurl di kode tidak berubah. Alih-alih menebak, ekstrak URL anchor dari halaman bermasalah, lalu bandingkan k dan co dengan yang dikirim ke solver — biasanya dalam hitungan menit ketahuan apakah domainnya tidak cocok atau sitekey sudah diganti di sisi target.
Catatan kepatuhan: pastikan halaman yang Anda proses memang milik sendiri atau sudah diotorisasi untuk diakses — UU Pelindungan Data Pribadi dan UU ITE tetap berlaku untuk data yang diproses lewat scraping.
Di luar kasus seperti itu, sebagian besar alur kerja otomatisasi tidak butuh URL anchor atau bframe — API CaptchaAI standar cukup dengan sitekey dan pageurl. Ekstraksi URL iframe ini baru berguna untuk empat hal:
- Memastikan sitekey yang dikirim sudah benar
- Mengenali versi reCAPTCHA yang sedang dihadapi
- Mencocokkan origin untuk implementasi yang ketat terhadap domain
- Men-debug kegagalan solve yang berulang
1. Memastikan sitekey yang dikirim sudah benar
Saat sebuah halaman punya beberapa instance reCAPTCHA atau sitekey-nya dimuat secara dinamis lewat JavaScript:
# Extract sitekey from anchor URL when it's not in the page HTML
iframes = extract_recaptcha_iframes(url)
sitekey = iframes["site_key"] # Reliably present in the iframe URL
2. Mengenali versi reCAPTCHA yang sedang dihadapi
# The anchor URL reveals the exact reCAPTCHA version
if "/api2/anchor" in anchor_url:
recaptcha_type = "v2"
elif "/enterprise/anchor" in anchor_url:
recaptcha_type = "enterprise"
3. Mencocokkan origin untuk implementasi yang ketat terhadap domain
# Decode the origin from the co parameter
origin = decode_co_parameter(iframes["co"])
# Use this origin as the pageurl for the solver
4. Men-debug kegagalan solve
Saat token ditolak berulang, bandingkan parameter URL anchor dengan permintaan yang Anda kirim ke solver — selisihnya sering langsung terlihat:
def debug_solve_params(anchor_url, solver_pageurl, solver_sitekey):
"""Compare anchor params with solver request to find mismatches."""
parsed = urlparse(anchor_url)
params = parse_qs(parsed.query)
issues = []
# Check sitekey
anchor_key = params.get("k", [None])[0]
if anchor_key != solver_sitekey:
issues.append(f"Sitekey mismatch: anchor={anchor_key}, solver={solver_sitekey}")
# Check origin
co = params.get("co", [None])[0]
if co:
origin = base64.b64decode(co.rstrip(".") + "==").decode()
solver_parsed = urlparse(solver_pageurl)
solver_origin = f"{solver_parsed.scheme}://{solver_parsed.netloc}"
if origin != solver_origin:
issues.append(f"Origin mismatch: anchor={origin}, solver={solver_origin}")
return issues if issues else ["No mismatches found"]
Solve reCAPTCHA standar tanpa ekstraksi iframe (direkomendasikan)
Untuk hampir semua kasus, lewati ekstraksi iframe sama sekali dan kirim task langsung ke CaptchaAI:
import requests
import time
API_KEY = "YOUR_API_KEY"
# All you need: sitekey + pageurl
submit = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": "6LcR_RsTAAAAAN_r0GEkGBfq3L7KmU5JbPHJtwNp",
"pageurl": "https://staging.example.com/qa-login",
"json": 1,
})
task_id = submit.json()["request"]
for _ in range(60):
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.get("status") == 1:
token = result["request"]
print(f"Token: {token[:50]}...")
break
Ekstraksi anchor/bframe hanya masuk agenda ketika solve standar gagal dan Anda perlu menyelidiki lebih dalam soal sitekey atau domain. Sebagai referensi cepat:
| Gejala | Kemungkinan penyebab | Langkah lanjutan |
|---|---|---|
| Token selalu ditolak, sitekey di kode tidak pernah berubah | Domain di co tidak cocok dengan pageurl |
Decode co, bandingkan dengan pageurl yang dikirim |
Sitekey None/kosong saat scraping halaman |
Sitekey dimuat dinamis lewat JavaScript | Ekstrak dari URL anchor, bukan dari atribut HTML statis |
| Iframe anchor maupun bframe sama sekali tidak ditemukan | Halaman belum selesai render saat diambil | Tambah jeda/WebDriverWait, atau cek apakah reCAPTCHA server-rendered |
Pertanyaan yang sering diajukan
Apakah cara ekstraksi ini juga berlaku untuk reCAPTCHA v2 Enterprise?
Ya, dengan satu beda kecil. reCAPTCHA v2 Enterprise memuat iframe anchor lewat path /enterprise/anchor, bukan /api2/anchor — parameternya (k, co, v, hl) tetap sama, cukup sesuaikan regex pencarian iframe di skrip Anda. reCAPTCHA v3 tidak punya bframe sama sekali karena tidak menampilkan tantangan visual.
Perlukah saya kirim URL anchor atau bframe ke CaptchaAI saat submit task solve?
Tidak. CaptchaAI hanya butuh sitekey dan pageurl — solver menangani interaksi anchor dan bframe secara internal. Mengekstrak kedua URL ini berguna untuk debugging, tapi bukan syarat untuk solve.
Script ekstraksi saya tidak menemukan iframe anchor maupun bframe sama sekali, kenapa?
Paling sering penyebabnya reCAPTCHA belum sempat dimuat saat HTML diambil. Untuk scraping statis, pastikan halaman memang server-render reCAPTCHA, bukan inject via JavaScript. Untuk Selenium, ganti time.sleep(3) tetap dengan WebDriverWait yang menunggu elemen iframe benar-benar muncul — jeda tetap sering terlalu pendek pada koneksi lambat.
Kenapa iframe bframe kadang tidak kelihatan saat saya inspect element manual di browser?
Iframe bframe dibuat secara dinamis oleh JavaScript reCAPTCHA hanya setelah pengguna mengklik checkbox dan tantangan gambar dipicu — jadi tidak ada di HTML awal. Anda perlu Selenium atau Puppeteer untuk berinteraksi dengan widget lebih dulu sebelum bisa mengambil URL bframe-nya.
Amankah menjalankan ekstraksi ini dari server dengan koneksi terbatas?
Aman, tapi sesuaikan timeout-nya. Contoh kode di atas memakai timeout=15 detik — cukup longgar untuk kebanyakan koneksi, tapi kalau server Anda sering mengalami latensi tinggi (di luar region cloud utama, atau jaringan mobile-first), naikkan ke 20–30 detik dan tambahkan retry sederhana sebelum menganggap ekstraksi gagal.
Ringkasan
reCAPTCHA dirender dalam dua iframe: anchor (widget checkbox) dan bframe (tantangan gambar). Untuk solve standar dengan CaptchaAI, Anda hanya perlu sitekey dan pageurl — tidak perlu menyentuh ekstraksi iframe sama sekali. Tiga hal yang perlu diingat:
- Anchor selalu ada di HTML; bframe baru muncul setelah checkbox diklik dan tantangan dipicu.
- Parameter
co(origin terenkode) adalah alat debugging paling berguna untuk kasus token ditolak karena domain mismatch. - Ekstraksi URL iframe hanya relevan saat solve standar gagal atau sitekey dimuat secara dinamis.
Artikel Terkait
- Cara solve callback reCAPTCHA v2 lewat API
- Turnstile dan reCAPTCHA v2 di situs yang sama
- Cara kerja mekanisme callback reCAPTCHA v2