Playwright tidak menyelesaikan CAPTCHA sendiri — dan memang bukan itu tugasnya. Playwright mengemudikan browser, sementara CaptchaAI menyelesaikan tantangan CAPTCHA di sisi server, lalu Anda menyuntikkan token hasilnya kembali ke halaman. Pemisahan tanggung jawab ini membuat alur E2E testing maupun scraping tetap stabil di Chromium, Firefox, dan WebKit tanpa Anda perlu mengklik gambar apa pun secara manual.
Artikel ini menunjukkan pola yang sama untuk reCAPTCHA v2/v3 dan Cloudflare Turnstile, dengan contoh Python (sync dan async) serta Node.js yang bisa langsung Anda pakai.
Cara kerja alurnya
Apa pun tipe CAPTCHA-nya, empat langkahnya selalu sama:
- Ekstrak sitekey dari halaman menggunakan selector Playwright (
data-sitekey). - Kirim task ke endpoint
in.phpCaptchaAI dan simpan task ID yang dikembalikan. - Polling ke
res.phpsampai token siap. - Suntikkan token ke elemen respons yang tepat, lalu submit form.
Playwright hanya bertanggung jawab pada langkah 1 dan 4 — membaca DOM dan menulis kembali token. Langkah 2 dan 3 sepenuhnya berada di API CaptchaAI, jadi pola ini identik apakah Anda pakai Puppeteer, Selenium, atau Playwright.
Yang perlu disiapkan
| Kebutuhan | Detail |
|---|---|
| Python | pip install playwright requests lalu playwright install |
| Node.js | npm install playwright axios |
| Kunci API CaptchaAI | Dari captchaai.com |
Python: integrasi Playwright + CaptchaAI
Menyiapkan fungsi solver
Fungsi ini membungkus langkah 2 dan 3: kirim sitekey ke in.php, lalu polling res.php setiap 5 detik sampai token terselesaikan atau batas waktu tercapai.
from playwright.sync_api import sync_playwright
import requests
import time
API_KEY = "YOUR_API_KEY"
def solve_recaptcha(site_key, page_url):
resp = requests.get("https://ocr.captchaai.com/in.php", params={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": site_key,
"pageurl": page_url
})
if not resp.text.startswith("OK|"):
raise Exception(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": API_KEY, "action": "get", "id": task_id
})
if result.text == "CAPCHA_NOT_READY": continue
if result.text.startswith("OK|"): return result.text.split("|")[1]
raise Exception(result.text)
raise TimeoutError()
Contoh login lengkap
Di sini Playwright mengisi form, mendeteksi .g-recaptcha, memanggil solver, lalu menulis token ke g-recaptcha-response sebelum submit. Perhatikan bahwa token disuntikkan ke elemen textarea respons, bukan ke widget yang terlihat.
def login_with_captcha(url, username, password):
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
context = browser.new_context(
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
)
page = context.new_page()
page.goto(url)
# Fill login form
page.fill("#username", username)
page.fill("#password", password)
# Check for reCAPTCHA
recaptcha = page.query_selector(".g-recaptcha")
if recaptcha:
site_key = recaptcha.get_attribute("data-sitekey")
print(f"Solving reCAPTCHA: {site_key}")
token = solve_recaptcha(site_key, page.url)
# Inject token
page.evaluate(f"""
document.getElementById('g-recaptcha-response').innerHTML = '{token}';
document.getElementById('g-recaptcha-response').style.display = '';
""")
# Submit
page.click('button[type="submit"]')
page.wait_for_load_state("networkidle")
print(f"Current URL: {page.url}")
content = page.content()
browser.close()
return content
result = login_with_captcha(
"https://staging.example.com/qa-login",
"user@example.com",
"password123"
)
Versi asinkron untuk banyak halaman
Untuk menjalankan banyak halaman sekaligus, versi async dengan aiohttp menjaga event loop tetap bebas selama menunggu polling — cocok saat satu worker mengurus puluhan tab paralel.
from playwright.async_api import async_playwright
import aiohttp
import asyncio
async def solve_recaptcha_async(site_key, page_url):
async with aiohttp.ClientSession() as session:
params = {
"key": API_KEY, "method": "userrecaptcha",
"googlekey": site_key, "pageurl": page_url
}
async with session.get("https://ocr.captchaai.com/in.php", params=params) as resp:
text = await resp.text()
task_id = text.split("|")[1]
for _ in range(60):
await asyncio.sleep(5)
params = {"key": API_KEY, "action": "get", "id": task_id}
async with session.get("https://ocr.captchaai.com/res.php", params=params) as resp:
text = await resp.text()
if text == "CAPCHA_NOT_READY": continue
if text.startswith("OK|"): return text.split("|")[1]
raise Exception(text)
raise TimeoutError()
async def main():
async with async_playwright() as p:
browser = await p.chromium.launch(headless=True)
page = await browser.new_page()
await page.goto("https://example.com/form")
site_key = await page.get_attribute(".g-recaptcha", "data-sitekey")
token = await solve_recaptcha_async(site_key, page.url)
await page.evaluate(f"document.getElementById('g-recaptcha-response').innerHTML = '{token}'")
await page.click('button[type="submit"]')
await browser.close()
asyncio.run(main())
Node.js: integrasi Playwright + CaptchaAI
Pola yang sama diterjemahkan langsung ke Node.js dengan axios. Solver melakukan polling dalam loop, lalu token disuntikkan lewat page.evaluate.
const { chromium } = require("playwright");
const axios = require("axios");
const API_KEY = "YOUR_API_KEY";
async function solveRecaptcha(siteKey, pageUrl) {
const submit = await axios.get("https://ocr.captchaai.com/in.php", {
params: {
key: API_KEY,
method: "userrecaptcha",
googlekey: siteKey,
pageurl: pageUrl,
},
});
const taskId = submit.data.split("|")[1];
while (true) {
await new Promise((r) => setTimeout(r, 5000));
const result = await axios.get("https://ocr.captchaai.com/res.php", {
params: { key: API_KEY, action: "get", id: taskId },
});
if (result.data === "CAPCHA_NOT_READY") continue;
if (result.data.startsWith("OK|")) return result.data.split("|")[1];
throw new Error(result.data);
}
}
(async () => {
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage();
await page.goto("https://staging.example.com/qa-login");
// Fill form
await page.fill("#username", "user@example.com");
await page.fill("#password", "password123");
// Solve CAPTCHA
const siteKey = await page.getAttribute(".g-recaptcha", "data-sitekey");
if (siteKey) {
const token = await solveRecaptcha(siteKey, page.url());
await page.evaluate(
(t) => (document.getElementById("g-recaptcha-response").innerHTML = t),
token
);
}
// Submit
await page.click('button[type="submit"]');
await page.waitForLoadState("networkidle");
console.log("Logged in:", page.url());
await browser.close();
})();
Menangani Cloudflare Turnstile
Turnstile mengikuti alur yang identik dengan reCAPTCHA — deteksi container, ekstrak sitekey, kirim task, lalu polling. Hanya dua detail yang berbeda:
methodberganti dariuserrecaptchamenjaditurnstile.- Token hasil disuntikkan ke field
cf-turnstile-response, bukang-recaptcha-response.
Deteksi container .cf-turnstile, kirim sitekey, lalu jalankan solver seperti biasa.
# Detect Turnstile
turnstile = page.query_selector(".cf-turnstile")
if turnstile:
site_key = turnstile.get_attribute("data-sitekey")
resp = requests.get("https://ocr.captchaai.com/in.php", params={
"key": API_KEY, "method": "turnstile",
"sitekey": site_key, "pageurl": page.url
})
task_id = resp.text.split("|")[1]
# Poll and inject...
Playwright vs Selenium vs Puppeteer
Ketiganya bisa memakai API CaptchaAI yang sama. Perbedaannya ada pada cakupan bahasa, browser, dan seberapa banyak penungguan yang harus Anda tulis sendiri.
| Fitur | Playwright | Selenium | Puppeteer |
|---|---|---|---|
| Bahasa | Python, Node.js, C#, Java | Python, Java, C#, Ruby, JS | Node.js |
| Browser | Chromium, Firefox, WebKit | Chrome, Firefox, Edge, Safari | Chromium |
| Auto-wait | ✅ Bawaan | ⏳ Penungguan manual | ⚠️ Sebagian |
| Intersepsi jaringan | ✅ | ⚠️ Terbatas | ✅ |
| Integrasi CaptchaAI | ✅ API yang sama | ✅ API yang sama | ✅ API yang sama |
CaptchaAI bekerja identik dengan ketiganya: ekstrak sitekey, selesaikan lewat API, suntikkan token. Jadi pemilihan framework murni soal preferensi tim, bukan soal kompatibilitas solver.
Mengatasi masalah umum
| Masalah | Perbaikan |
|---|---|
page.query_selector mengembalikan null |
CAPTCHA dimuat dinamis; pakai page.wait_for_selector() |
| Token tidak tersimpan | Cek apakah textarea respons punya ID yang berbeda |
| Playwright crash di Docker | Instal dependensi browser: playwright install-deps |
| CAPTCHA muncul lagi setelah diselesaikan | Situs mungkin butuh eksekusi callback; picu lewat page.evaluate() |
Biaya dan thread untuk otomatisasi paralel
Untuk tim scraping dan agensi price-monitoring di Indonesia — yang banyak mengambil pekerjaan freelance bervolume tinggi — model harga jadi pertimbangan nyata. CaptchaAI menagih per thread konkuren, bukan per solve: satu thread adalah satu CAPTCHA yang sedang dikerjakan, dan setiap paket memberi solve tanpa batas per thread selama sebulan.
Aturannya sederhana: jumlah thread yang Anda butuhkan kira-kira sebanyak instance Playwright yang berjalan bersamaan. Jadikan alokasi thread sebagai patokan memilih paket:
- BASIC — $15/bulan, 5 thread: cukup untuk sekitar 5 tab paralel.
- STANDARD — $30/bulan, 15 thread: pool worker menengah.
- ADVANCE — $90/bulan, 50 thread: scraping bervolume tinggi.
Karena tidak ada biaya per solve, lonjakan volume tidak membuat tagihan meledak — hanya kecepatan total yang dibatasi alokasi thread Anda.
Saat men-deploy worker headless di region seperti AWS ap-southeast-1 (Singapura) atau ap-southeast-3 (Jakarta), atur range(60) pada loop polling agar sesuai latensi jaringan Anda. Dan sesuai UU Pelindungan Data Pribadi (UU 27/2022), pastikan Anda hanya memproses data yang memang berhak Anda akses — hindari data pribadi di balik login milik orang lain.
Pertanyaan umum
Apakah CaptchaAI bisa menyelesaikan hCaptcha lewat Playwright?
Belum. hCaptcha dan FunCaptcha (Arkose Labs) tidak didukung saat ini, jadi jangan andalkan CaptchaAI untuk keduanya. Yang didukung mencakup reCAPTCHA v2/v3, Cloudflare Turnstile dan Challenge, GeeTest v3, serta image/OCR — plus CaptchaFox (beta), Friendly Captcha (beta), dan Lemin (beta).
Berapa thread yang saya butuhkan untuk menjalankan banyak instance Playwright paralel?
Kira-kira satu thread per instance yang berjalan bersamaan. Karena penagihan berbasis thread konkuren dengan solve tanpa batas, Anda cukup menyamakan jumlah thread dengan tingkat paralelisme worker — bukan dengan total CAPTCHA yang diselesaikan sebulan.
Ke elemen mana token harus disuntikkan?
Untuk reCAPTCHA, tulis token ke textarea#g-recaptcha-response; untuk Turnstile, ke field cf-turnstile-response. Menyuntik ke widget yang terlihat tidak berpengaruh — form membaca nilai dari field respons tersembunyi inilah yang divalidasi server.
Bagaimana mengatur timeout agar Playwright menunggu token siap?
Waktu penyelesaian ada di sisi CaptchaAI, bukan di penungguan DOM Playwright. Sesuaikan batas loop polling (range(60) × 5 detik ≈ 5 menit) agar solver punya cukup waktu, lalu baru panggil page.wait_for_load_state("networkidle") setelah token disuntik dan form disubmit.
Apakah pola yang sama berlaku di Firefox dan WebKit, bukan hanya Chromium?
Ya. Ekstraksi sitekey dan penyuntikan token bekerja pada DOM, bukan pada mesin browser tertentu, jadi ganti p.chromium dengan p.firefox atau p.webkit tanpa mengubah logika solver sama sekali. Inilah salah satu alasan Playwright populer untuk pengujian lintas-browser.
Panduan terkait
- Menangani CAPTCHA di Selenium dengan Python
- Menyelesaikan CAPTCHA di Puppeteer dengan Node.js
- Otomatisasi login dengan CAPTCHA