Playwright otomasyonunuz bir reCAPTCHA v2, Cloudflare Turnstile ya da resim CAPTCHA'sına takıldığında akışı durdurmanıza gerek yok: CaptchaAI API'sini asenkron olarak çağırıp dönen token'ı sayfaya enjekte edersiniz. Bu rehber, aiohttp tabanlı tek bir çözüm fonksiyonundan uçtan uca bir otomasyon sınıfına kadar tüm entegrasyonu çalışan Python koduyla gösterir.
İstanbul'da müşteri projeleri yürüten bir otomasyon geliştiricisi için tablo tanıdıktır: bir e-ticaret ödeme akışının QA testini yazarsınız, test ortamı reCAPTCHA veya Turnstile ile korunur ve senaryo tam orada durur. CaptchaAI'nin thread tabanlı ve USD sabit fiyatlı planları (BASIC $15/ay, 5 thread) bu adımı otomatikleştirmenin öngörülebilir yolunu sunar — TL kuru dalgalansa da aylık maliyetiniz sabit kalır.
Kurulum ve önkoşullar
Tek ihtiyacınız Python 3.8+ ve iki paket: asenkron HTTP istekleri için aiohttp, tarayıcı otomasyonu için Playwright. Chromium'u bir kez indirmeniz yeterli.
pip install playwright aiohttp
playwright install chromium
Asenkron CaptchaAI çözüm fonksiyonu
Entegrasyonun kalbi tek bir asenkron fonksiyondur: görevi in.php uç noktasına gönderir, ardından sonucu res.php üzerinden json=1 ile sorgular. Playwright zaten asenkron çalıştığı için aiohttp doğal bir eşleşmedir.
import aiohttp
import asyncio
API_KEY = "YOUR_API_KEY"
async def solve_captcha(method, **params):
"""Async CaptchaAI solver for Playwright workflows."""
async with aiohttp.ClientSession() as session:
# Submit task
submit_data = {
"key": API_KEY,
"method": method,
"json": 1,
**params,
}
async with session.post("https://ocr.captchaai.com/in.php", data=submit_data) as resp:
data = await resp.json(content_type=None)
if data.get("status") != 1:
raise Exception(f"Submit error: {data.get('request')}")
task_id = data["request"]
# Poll for result
for _ in range(30):
await asyncio.sleep(5)
async with session.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY,
"action": "get",
"id": task_id,
"json": 1,
}) as resp:
result = await resp.json(content_type=None)
if result.get("status") == 1:
return result["request"]
if result.get("request") == "ERROR_CAPTCHA_UNSOLVABLE":
raise Exception("CAPTCHA unsolvable")
raise TimeoutError("Solve timed out")
Playwright tarayıcısını QA testleri için yapılandırma
Tarayıcıyı sabit bir user-agent, gerçekçi bir viewport ve en-US locale ile başlatın. Aşağıdaki yapılandırma, QA test ortamınızda tutarlı ve tekrarlanabilir davranış için başlangıç ayarlarını gösterir.
from playwright.async_api import async_playwright
async def create_browser():
"""Launch Playwright browser with QA testi-configuredion settings."""
pw = await async_playwright().start()
browser = await pw.chromium.launch(
headless=False,
args=[
"--no-sandbox",
],
)
context = await browser.new_context(
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",
viewport={"width": 1920, "height": 1080},
locale="en-US",
)
# Remove Playwright detection signals
await context.add_init_script("""
Object.defineProperty(navigator, 'webdriver', {get: () => undefined});
delete navigator.__proto__.webdriver;
""")
page = await context.new_page()
return pw, browser, context, page
Playwright ile reCAPTCHA v2 çözme
reCAPTCHA v2 akışı dört adımdır: sayfadan sitekey'i çıkarın, googlekey ve pageurl ile CaptchaAI'ye gönderin, dönen token'ı g-recaptcha-response alanına yazın ve varsa callback'i tetikleyin.
import re
async def solve_recaptcha_v2_playwright(page, url):
"""Complete reCAPTCHA v2 solve in Playwright."""
await page.goto(url, wait_until="networkidle")
# Extract sitekey from the page
content = await page.content()
match = re.search(r'data-sitekey=["\']([A-Za-z0-9_-]{40})["\']', content)
if not match:
raise ValueError("reCAPTCHA sitekey not found")
sitekey = match.group(1)
print(f"Sitekey: {sitekey}")
# Solve via CaptchaAI
token = await solve_captcha(
"userrecaptcha",
googlekey=sitekey,
pageurl=url,
)
print(f"Token: {token[:50]}...")
# Inject token
await page.evaluate(f"""() => {{
document.getElementById('g-recaptcha-response').value = '{token}';
document.getElementById('g-recaptcha-response').style.display = 'block';
}}""")
# Trigger callback if available
await page.evaluate(f"""() => {{
if (typeof ___grecaptcha_cfg !== 'undefined') {{
var clients = ___grecaptcha_cfg.clients;
for (var key in clients) {{
var client = clients[key];
try {{
Object.keys(client).forEach(function(k) {{
if (client[k] && client[k].callback) {{
client[k].callback('{token}');
}}
}});
}} catch(e) {{}}
}}
}}
}}""")
# Submit form
await page.click("button[type='submit'], input[type='submit']")
await page.wait_for_load_state("networkidle")
return token
Playwright ile Cloudflare Turnstile çözme
Turnstile akışı benzerdir; tek fark parametrenin sitekey, token alanının ise cf-turnstile-response olmasıdır. Token'ı sayfadaki gizli input alanlarına yazıp formu gönderin.
async def solve_turnstile_playwright(page, url):
"""Complete Turnstile solve in Playwright."""
await page.goto(url, wait_until="networkidle")
content = await page.content()
# Extract sitekey
match = re.search(r'data-sitekey=["\']([0-9x][A-Za-z0-9_-]+)["\']', content)
if not match:
match = re.search(r"sitekey\s*:\s*['\"]([0-9x][A-Za-z0-9_-]+)['\"]", content)
if not match:
raise ValueError("Turnstile sitekey not found")
sitekey = match.group(1)
print(f"Turnstile sitekey: {sitekey}")
# Solve via CaptchaAI
token = await solve_captcha(
"turnstile",
sitekey=sitekey,
pageurl=url,
)
# Inject token into hidden inputs
await page.evaluate(f"""() => {{
document.querySelectorAll('[name="cf-turnstile-response"]')
.forEach(el => el.value = '{token}');
}}""")
# Submit
await page.click("button[type='submit'], input[type='submit']")
await page.wait_for_load_state("networkidle")
return token
Playwright ile resim (image) CAPTCHA çözme
Klasik resim CAPTCHA'ları için öğenin ekran görüntüsünü alın, base64'e çevirin ve base64 yöntemiyle CaptchaAI'ye gönderin; dönen metni ilgili input alanına yazın.
async def solve_image_captcha_playwright(page, captcha_selector):
"""Solve image CAPTCHA visible on the page."""
captcha_element = page.locator(captcha_selector)
# Screenshot the CAPTCHA image
img_bytes = await captcha_element.screenshot()
import base64
img_base64 = base64.b64encode(img_bytes).decode()
# Solve via CaptchaAI
answer = await solve_captcha("base64", body=img_base64)
print(f"Answer: {answer}")
# Type the answer
captcha_input = page.locator("input[name='captcha'], input[name='code'], input.captcha-input")
await captcha_input.fill(answer)
return answer
Ağ isteklerini yakalama (request interception)
Playwright, ağ isteklerini yakalama konusunda güçlüdür. Bazı sitelerde sitekey HTML içine gömülü değildir; böyle durumlarda parametreleri doğrudan API çağrılarından yakalayabilirsiniz:
async def intercept_captcha_params(page, url):
"""Intercept network requests to find CAPTCHA parameters."""
captcha_params = {}
async def handle_request(route, request):
if "recaptcha" in request.url or "turnstile" in request.url:
from urllib.parse import urlparse, parse_qs
parsed = urlparse(request.url)
params = parse_qs(parsed.query)
captcha_params.update(params)
print(f"Intercepted: {request.url}")
await route.continue_()
await page.route("**/*", handle_request)
await page.goto(url, wait_until="networkidle")
await page.unroute("**/*")
return captcha_params
Uçtan uca otomasyon sınıfı
Yukarıdaki parçaları tek bir sınıfta toplamak, üretimde bakımı belirgin biçimde kolaylaştırır. PlaywrightCaptchaSolver tarayıcıyı başlatır, sayfadaki CAPTCHA türünü algılar, çözer, formu doldurur ve gönderir. detect_captcha metodu reCAPTCHA v2, reCAPTCHA v3, Turnstile ve resim CAPTCHA'sını ayırt eder; böylece tek bir giriş noktasıyla farklı sayfa tiplerini aynı akışta işleyebilirsiniz. Çözüm zaman aşımına uğrarsa fonksiyon TimeoutError fırlatır, bu yüzden çağrıyı üretimde bir try/except bloğuyla sarmalayın.
import re
import asyncio
import aiohttp
import base64
from playwright.async_api import async_playwright
API_KEY = "YOUR_API_KEY"
class PlaywrightCaptchaSolver:
"""Complete Playwright + CaptchaAI automation class."""
def __init__(self, api_key, headless=False):
self.api_key = api_key
self.headless = headless
self.pw = None
self.browser = None
self.context = None
self.page = None
async def start(self):
"""Initialize the browser."""
self.pw = await async_playwright().start()
self.browser = await self.pw.chromium.launch(
headless=self.headless,
args=["--no-sandbox"],
)
self.context = await self.browser.new_context(
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",
viewport={"width": 1920, "height": 1080},
)
await self.context.add_init_script(
"Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"
)
self.page = await self.context.new_page()
async def stop(self):
"""Close the browser."""
if self.browser:
await self.browser.close()
if self.pw:
await self.pw.stop()
async def navigate(self, url):
"""Navigate and wait for page to load."""
await self.page.goto(url, wait_until="networkidle")
async def detect_captcha(self):
"""Detect which CAPTCHA type is present."""
content = await self.page.content()
if re.search(r'data-sitekey=["\'][A-Za-z0-9_-]{40}["\']', content):
if "recaptcha" in content.lower():
return "recaptcha_v2"
if "cf-turnstile" in content or "challenges.cloudflare.com/turnstile" in content:
return "turnstile"
if re.search(r"render=[A-Za-z0-9_-]{40}", content):
return "recaptcha_v3"
img_count = await self.page.locator(
"img.captcha, img[alt*='captcha'], img[src*='captcha']"
).count()
if img_count > 0:
return "image"
return None
async def solve_and_submit(self, url, form_data=None):
"""Full workflow: navigate, detect, solve, fill, submit."""
await self.navigate(url)
captcha_type = await self.detect_captcha()
if captcha_type:
print(f"Detected: {captcha_type}")
await self._solve(captcha_type)
if form_data:
for name, value in form_data.items():
try:
await self.page.fill(f"[name='{name}']", value)
except Exception:
pass
await self.page.click("button[type='submit'], input[type='submit']")
await self.page.wait_for_load_state("networkidle")
return self.page.url
async def _solve(self, captcha_type):
content = await self.page.content()
url = self.page.url
if captcha_type == "recaptcha_v2":
match = re.search(r'data-sitekey=["\']([A-Za-z0-9_-]{40})["\']', content)
token = await self._api_solve("userrecaptcha", googlekey=match.group(1), pageurl=url)
await self.page.evaluate(f"""() => {{
document.getElementById('g-recaptcha-response').value = '{token}';
}}""")
elif captcha_type == "turnstile":
match = re.search(r'data-sitekey=["\']([0-9x][A-Za-z0-9_-]+)["\']', content)
token = await self._api_solve("turnstile", sitekey=match.group(1), pageurl=url)
await self.page.evaluate(f"""() => {{
document.querySelectorAll('[name="cf-turnstile-response"]')
.forEach(el => el.value = '{token}');
}}""")
elif captcha_type == "image":
img = self.page.locator("img.captcha, img[alt*='captcha'], img[src*='captcha']").first
img_bytes = await img.screenshot()
answer = await self._api_solve("base64", body=base64.b64encode(img_bytes).decode())
await self.page.fill("input[name='captcha'], input[name='code']", answer)
async def _api_solve(self, method, **params):
async with aiohttp.ClientSession() as session:
async with session.post("https://ocr.captchaai.com/in.php", data={
"key": self.api_key, "method": method, "json": 1, **params,
}) as resp:
data = await resp.json(content_type=None)
if data.get("status") != 1:
raise Exception(f"Submit error: {data.get('request')}")
task_id = data["request"]
for _ in range(30):
await asyncio.sleep(5)
async with session.get("https://ocr.captchaai.com/res.php", params={
"key": self.api_key, "action": "get", "id": task_id, "json": 1,
}) as resp:
result = await resp.json(content_type=None)
if result.get("status") == 1:
return result["request"]
raise TimeoutError("Solve timed out")
# Usage
async def main():
solver = PlaywrightCaptchaSolver(API_KEY)
await solver.start()
try:
result = await solver.solve_and_submit(
"https://staging.example.com/qa-login",
form_data={"email": "user@example.com", "password": "pass123"},
)
print(f"Result: {result}")
finally:
await solver.stop()
asyncio.run(main())
CAPTCHA otomasyonu için Playwright ve Selenium karşılaştırması
| Özellik | Playwright | Selenium |
|---|---|---|
| Yerel asenkron | Evet | Hayır (threading gerektirir) |
| Hazır varsayılanlar | Daha iyi | Daha fazla ayar gerektirir |
| Hız | Daha hızlı | Daha yavaş sayfa yüklemeleri |
| İstek yakalama (interception) | Yerleşik | Proxy/eklenti gerektirir |
| Çoklu tarayıcı | Chromium, Firefox, WebKit | Chrome, Firefox, Edge, Safari |
| API stili | Promise tabanlı, modern | Zorunlu (imperative), geleneksel |
Sorun giderme
| Sorun | Sebep | Çözüm |
|---|---|---|
page.evaluate başarısız oluyor |
Sayfa içeriği henüz yüklenmedi | wait_until="networkidle" ile bekleyin |
| Token enjeksiyonu çalışmıyor | Yanlış eleman seçici | Doğru öğeyi bulmak için page.content() çıktısını inceleyin |
| Playwright tespit ediliyor | add_init_script başlatma betiği eksik |
Başlatma betiğini add_init_script içinde ekleyin |
networkidle'de zaman aşımı |
Sonu gelmeyen sorgulama betikleri | Bunun yerine wait_until="domcontentloaded" kullanın |
| Resim ekran görüntüsü boş | Öğe görünür değil | Görünüme kaydırın: await element.scroll_into_view_if_needed() |
Sık sorulan sorular
Playwright ile CAPTCHA çözmek için hangi CaptchaAI planı yeterli?
Tek bir Playwright betiği için BASIC ($15/ay, 5 thread) çoğu durumda yeterlidir; 5 thread, aynı anda 5 CAPTCHA çözümü demektir. Paralel çok sayıda tarayıcı çalıştırıyorsanız STANDARD ($30/ay, 15 thread) veya ADVANCE ($90/ay, 50 thread) planına geçin. Faturalama thread başınadır, çözüm başına değil.
Playwright headless modda CAPTCHA çözebilir mi?
Evet. launch() içinde headless=True ayarlayın. Bazı siteler headless tarayıcıyı ayrı değerlendirdiğinden her iki yapılandırmayı da test edin. CaptchaAI çözümü kendi altyapısında yaptığı için headless veya headed olması çözüm başarısını etkilemez.
Token'ı enjekte ettim ama form gönderilmiyor, sorun ne?
Çoğu sitede token alanını doldurmak tek başına yetmez; reCAPTCHA callback'ini de tetiklemeniz gerekir. Yukarıdaki reCAPTCHA v2 örneğindeki ___grecaptcha_cfg bloğu tam olarak bunu yapar. Alan gizliyse style.display = 'block' satırını koruyun.
CaptchaAI hangi CAPTCHA türlerini destekliyor?
Bu rehberdeki akış reCAPTCHA v2/v3, Cloudflare Turnstile ve resim (base64) CAPTCHA'larını kapsar. hCaptcha ve FunCaptcha henüz desteklenmiyor; GeeTest v4 için destek çok yakında. Desteklenen türlerin güncel listesini panelden doğrulayın.
Aynı anda kaç sayfayı paralel çözebilirim?
Sınırı planınızdaki thread sayısı belirler. Playwright tarafında birden çok context/page açıp asyncio.gather ile paralel çalıştırabilirsiniz; CaptchaAI tarafında eşzamanlı çözüm sayısı thread kotanızla sınırlıdır.
Özet ve sonraki adımlar
Playwright'ın yerel asenkron yapısı, CaptchaAI API'siyle birleştiğinde algıla-çöz-gönder akışını tek bir yığında toplar. PlaywrightCaptchaSolver sınıfını kendi projenize kopyalayın, YOUR_API_KEY yerine kendi anahtarınızı yazın ve önce bir QA test ortamında deneyin. Thread tabanlı planlar sayesinde aylık maliyetiniz USD cinsinden öngörülebilir kalır. Otomasyonu gerçek verilerle çalıştıracaksanız, topladığınız kişisel verilerin KVKK kapsamına girebileceğini unutmayın; yetkili olduğunuz veri toplama ve QA iş akışlarında kalın.