Sitekey'i DOM'da aramayı bırakın: değer zaten ağ trafiğinde duruyor. Selenium Wire, tarayıcının yaptığı her HTTP isteğini Python nesnesi olarak önünüze koyduğu için recaptcha/api2/anchor isteğinin URL'sindeki k= parametresini, sayfa iframe'i çizmeyi bitirmeden okuyabilirsiniz. Tek satırlık bu fark, find_element zincirlerine dayanan entegrasyonları kırılgan olmaktan çıkarır.
Aşağıdaki Python sınıfı ağ trafiğinden CAPTCHA türünü tanıyor, görevi CaptchaAI'nin in.php uç noktasına gönderiyor, res.php'yi sorguluyor ve dönen token'ı forma yazıyor. Devamında başlık ekleme, istek kesme, HAR dışa aktarma ve eşzamanlılık planlaması var.
DOM sorgusu neden kırılır, ağ trafiği neden dayanıklıdır?
Klasik yaklaşım şudur: sayfayı açarsınız, [data-sitekey] seçicisini sorgularsınız, değeri alırsınız. Üretimde bu üç noktada tıkanır — ve tıkandığında testiniz "CAPTCHA yok" sanıp sessizce yanlış sonuç üretir.
- Widget iframe içinde yüklenir. Ana belgede
data-sitekeyyoktur; seçici boş döner. - Parametreler AJAX ile gelir. Sitekey, sayfa yüklendikten saniyeler sonra bir JSON yanıtının içinde iner.
- Sürüm ayrımı görünmez. DOM'a bakarak reCAPTCHA v2 ile v3'ü ayırmak zordur; ağ trafiğinde
anchorverender=istekleri bunu net biçimde söyler.
Ağ katmanı üçünün de dışındadır: tarayıcı hangi kaynağı çekiyorsa istek listesinde görünür, iframe içinde olması bunu değiştirmez.
Selenium Wire standart Selenium'a ne ekler?
| Yetenek | Standart Selenium | Selenium Wire |
|---|---|---|
| İstekleri okuma | Yok | Var |
| Başlık değiştirme | Sınırlı | Tam kontrol |
| Yanıtı yakalama | Yok | Var |
| Proxy desteği | Temel | Kimlik doğrulamalı proxy |
| Ağ günlüğü | Yalnızca DevTools | Python nesnesi |
| HAR dışa aktarma | Yok | Var |
Pratikte kazandığınız şey tek bir liste: driver.requests. Tüm istekleri başlık ve yanıt gövdeleriyle tutar.
Kurulum
pip install seleniumwire selenium webdriver-manager requests
Import satırı dışında mevcut Selenium kodunuz değişmez: from seleniumwire import webdriver yazdığınız anda aynı WebDriver API'si ağ yetenekleriyle çalışır.
Çözücü sınıfı: tanı, gönder, sorgula, enjekte et
Aşağıdaki sınıf dört işi üstleniyor ve bu dört adım her entegrasyonun iskeletidir:
detect_captcha_from_requests— istek listesini tarar,anchor/reload/challenges.cloudflare.comimzalarından türü ve sitekey'i çıkarır.solve_captcha— türe göremethod,googlekeyveyakeyvepageurlalanlarını doldurup görevi gönderir.- Sorgulama döngüsü —
res.php'yi beş saniyede bir sorgular;CAPCHA_NOT_READYyanıtında beklemeye devam eder. inject_token— dönen token'ıg-recaptcha-responseveyacf-turnstile-responsealanına yazar.
from seleniumwire import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
import requests
import time
import json
class SeleniumWireCaptchaSolver:
BASE_URL = "https://ocr.captchaai.com"
def __init__(self, api_key, proxy=None):
self.api_key = api_key
self.proxy = proxy
self.driver = None
def create_driver(self, headless=True):
options = webdriver.ChromeOptions()
if headless:
options.add_argument("--headless=new")
options.add_argument("--no-sandbox")
options.add_argument("--no-sandbox")
options.add_argument("--window-size=1920,1080")
wire_options = {}
if self.proxy:
wire_options["proxy"] = {
"http": self.proxy,
"https": self.proxy,
}
self.driver = webdriver.Chrome(
service=Service(ChromeDriverManager().install()),
options=options,
seleniumwire_options=wire_options,
)
return self.driver
def detect_captcha_from_requests(self):
"""Detect CAPTCHA type from intercepted network requests."""
for request in self.driver.requests:
url = request.url
if "recaptcha/api2/anchor" in url or "recaptcha/enterprise/anchor" in url:
# Extract sitekey from reCAPTCHA iframe URL
import re
match = re.search(r"k=([A-Za-z0-9_-]+)", url)
if match:
return {
"type": "recaptcha_v2",
"sitekey": match.group(1),
"page_url": self.driver.current_url,
}
if "recaptcha/api2/reload" in url or "recaptcha/enterprise/reload" in url:
return {
"type": "recaptcha_v3",
"sitekey": self._extract_v3_sitekey(),
"page_url": self.driver.current_url,
}
if "challenges.cloudflare.com" in url:
sitekey = self._extract_turnstile_sitekey()
if sitekey:
return {
"type": "turnstile",
"sitekey": sitekey,
"page_url": self.driver.current_url,
}
return None
def _extract_v3_sitekey(self):
for request in self.driver.requests:
if "recaptcha" in request.url and "render=" in request.url:
import re
match = re.search(r"render=([A-Za-z0-9_-]+)", request.url)
if match:
return match.group(1)
return self.driver.execute_script(
"return document.querySelector('[data-sitekey]')?.getAttribute('data-sitekey')"
)
def _extract_turnstile_sitekey(self):
return self.driver.execute_script(
"return document.querySelector('.cf-turnstile[data-sitekey]')?.getAttribute('data-sitekey')"
)
def solve_captcha(self, captcha_info):
"""Solve detected CAPTCHA via CaptchaAI API."""
params = {"key": self.api_key, "json": 1}
if captcha_info["type"] == "recaptcha_v2":
params.update({
"method": "userrecaptcha",
"googlekey": captcha_info["sitekey"],
"pageurl": captcha_info["page_url"],
})
elif captcha_info["type"] == "recaptcha_v3":
params.update({
"method": "userrecaptcha",
"googlekey": captcha_info["sitekey"],
"pageurl": captcha_info["page_url"],
"version": "v3",
"action": "verify",
})
elif captcha_info["type"] == "turnstile":
params.update({
"method": "turnstile",
"key": captcha_info["sitekey"],
"pageurl": captcha_info["page_url"],
})
# Submit
resp = requests.post(f"{self.BASE_URL}/in.php", data=params)
data = resp.json()
if data["status"] != 1:
raise Exception(f"Submit: {data['request']}")
task_id = data["request"]
# Poll
for _ in range(60):
time.sleep(5)
resp = requests.get(f"{self.BASE_URL}/res.php", params={
"key": self.api_key,
"action": "get",
"id": task_id,
"json": 1,
})
data = resp.json()
if data["request"] == "CAPCHA_NOT_READY":
continue
if data["status"] != 1:
raise Exception(f"Solve: {data['request']}")
return data["request"]
raise Exception("Timeout")
def inject_token(self, captcha_type, token):
"""Inject solved token into the page."""
if captcha_type in ("recaptcha_v2", "recaptcha_v3"):
self.driver.execute_script(f"""
document.querySelector('#g-recaptcha-response').value = '{token}';
// Try hidden textareas in iframes
document.querySelectorAll('[name="g-recaptcha-response"]').forEach(
el => el.value = '{token}'
);
""")
elif captcha_type == "turnstile":
self.driver.execute_script(f"""
const input = document.querySelector('[name="cf-turnstile-response"]');
if (input) input.value = '{token}';
""")
def close(self):
if self.driver:
self.driver.quit()
YOUR_API_KEY yer tutucusunu koda gömmeyin; ortam değişkeninden okuyun. Panelden aldığınız 32 karakterlik anahtar bütün türler için aynıdır.
Uçtan uca örnek: staging giriş formunun QA testi
Türkiye'deki ekiplerin çoğu bu akışı e-ticaret ve fintech tarafında kuruyor: her sürümden önce giriş formunun ve ödeme adımının kendi staging ortamında çalıştığı doğrulanmalı, formda da bir CAPTCHA duruyor. Aşağıdaki senaryo kendi staging adresinizde formu dolduruyor, CAPTCHA'yı ağ trafiğinden tanıyıp çözüyor ve gönderiyor.
def automate_login():
solver = SeleniumWireCaptchaSolver(
api_key="YOUR_API_KEY",
proxy="http://user:pass@proxy.example.com:8080"
)
try:
driver = solver.create_driver(headless=True)
# Navigate and wait for CAPTCHA resources to load
driver.get("https://staging.example.com/qa-login")
time.sleep(3)
# Fill form
driver.find_element("id", "email").send_keys("user@example.com")
driver.find_element("id", "password").send_keys("password123")
# Detect CAPTCHA from network requests
captcha = solver.detect_captcha_from_requests()
if captcha:
print(f"Found {captcha['type']} (sitekey: {captcha['sitekey'][:20]}...)")
token = solver.solve_captcha(captcha)
solver.inject_token(captcha["type"], token)
print(f"Token injected: {token[:50]}...")
# Submit
driver.find_element("id", "submit").click()
time.sleep(3)
print(f"Current URL: {driver.current_url}")
finally:
solver.close()
automate_login()
Gece çalışan bir iş için zamanlamayı Europe/Istanbul saat diliminde kurun ve test kullanıcılarını tr-TR verileriyle besleyin; böylece rapor saatleri ekibin mesai saatleriyle örtüşür.
İstekleri değiştirmenin üç pratik yolu
Her isteğe başlık eklemek
request_interceptor, sayfanın yaptığı her istekten önce çalışır. Ortak Accept-Language değerini veya QA ortamınızın beklediği özel başlığı buradan geçirin.
def add_headers_interceptor(request):
request.headers["X-Custom-Header"] = "value"
request.headers["Accept-Language"] = "en-US,en;q=0.9"
driver = solver.create_driver()
driver.request_interceptor = add_headers_interceptor
Gereksiz üçüncü taraf isteklerini kesmek
Analitik ve reklam istekleri CAPTCHA'nın yüklenmesini geciktirir, test süresini uzatır. request.abort() ile bunları en baştan kesebilirsiniz.
def block_trackers(request):
blocked = ["analytics", "tracking", "ads", "facebook.net", "doubleclick"]
if any(b in request.url for b in blocked):
request.abort()
driver.request_interceptor = block_trackers
Yanıt gövdesini test amaçlı değiştirmek
response_interceptor, kendi servisinizden dönen yapılandırmayı test sırasında değiştirmenizi sağlar — yalnızca sahibi olduğunuz staging servislerinde kullanın.
def modify_response(request, response):
if "captcha-config" in request.url:
# Modify CAPTCHA configuration response
import json
body = json.loads(response.body.decode("utf-8"))
body["difficulty"] = "easy"
response.body = json.dumps(body).encode("utf-8")
driver.response_interceptor = modify_response
Selenium Wire ile ağ katmanında hata ayıklama
Sınıf çalıştıktan sonra işin geri kalanı ağ katmanında geçer: sitekey'i JSON yanıtından çıkarmak, trafiği proxy üzerinden geçirmek ve sorunu kanıtlayan bir kayıt üretmek.
AJAX yanıtlarından sitekey çıkarma
Bazı siteler CAPTCHA yapılandırmasını HTML'e gömmez, ilk etkileşimden sonra bir JSON uç noktasından çeker. Aşağıdaki fonksiyon, verdiğiniz zaman aşımı boyunca driver.requests listesini tarayıp sitekey geçen ilk JSON yanıtını döndürür.
def extract_captcha_from_api(driver, timeout=10):
"""Watch network for CAPTCHA configuration API calls."""
start = time.time()
while time.time() - start < timeout:
for request in driver.requests:
if request.response and "captcha" in request.url.lower():
try:
data = json.loads(request.response.body.decode())
if "sitekey" in str(data) or "siteKey" in str(data):
return data
except (json.JSONDecodeError, UnicodeDecodeError):
pass
time.sleep(0.5)
return None
Birden fazla proxy ile çalışmak
Selenium Wire, kimlik doğrulamalı proxy'yi (user:pass@host:port) yerleşik destekler; standart Selenium'da eklenti isteyen bu ayar tek sözlükle biter. Sürücü örneği başına farklı bir çıkış tanımlayabilir, exclude_hosts ile statik kaynakları kapsam dışında bırakabilirsiniz.
from seleniumwire import webdriver
proxies = [
"http://user:pass@proxy1.example.com:8080",
"http://user:pass@proxy2.example.com:8080",
"http://user:pass@proxy3.example.com:8080",
]
current_proxy = [0]
def rotate_proxy(request):
proxy = proxies[current_proxy[0] % len(proxies)]
request.headers["Proxy-Authorization"] = None # Reset
current_proxy[0] += 1
wire_options = {
"proxy": {
"http": proxies[0],
"https": proxies[0],
}
}
driver = webdriver.Chrome(seleniumwire_options=wire_options)
CAPTCHA trafiğini tek tabloda görmek
Bir entegrasyon sessizce başarısız olduğunda ilk soru şudur: tarayıcı CAPTCHA kaynaklarını gerçekten çekti mi? Aşağıdaki fonksiyon ilgili isteklerin durum kodunu, içerik türünü ve boyutunu tek listede toplar.
def analyze_captcha_traffic(driver):
"""Analyze all CAPTCHA-related network traffic."""
captcha_requests = []
for request in driver.requests:
if any(k in request.url for k in ["recaptcha", "turnstile", "hcaptcha", "geetest"]):
captcha_requests.append({
"url": request.url,
"method": request.method,
"status": request.response.status_code if request.response else None,
"content_type": request.response.headers.get("Content-Type") if request.response else None,
"size": len(request.response.body) if request.response and request.response.body else 0,
})
print(f"\nCAPTCHA Network Traffic ({len(captcha_requests)} requests):")
for req in captcha_requests:
print(f" [{req['status']}] {req['method']} {req['url'][:80]}...")
return captcha_requests
HAR dışa aktarımı: destek biletine eklenecek dosya
Sorun sizde değil de hedef sayfanın davranışındaysa, ekran görüntüsü yerine HAR dosyası gönderin. Aşağıdaki fonksiyon istek ve yanıt başlıklarını standart HAR biçiminde kaydeder.
from seleniumwire.utils import decode
def export_har(driver, filename="captcha_traffic.har"):
"""Export HAR file for debugging CAPTCHA issues."""
import json
har = {
"log": {
"version": "1.2",
"entries": []
}
}
for request in driver.requests:
if not request.response:
continue
entry = {
"request": {
"method": request.method,
"url": request.url,
"headers": [{"name": k, "value": v} for k, v in request.headers.items()],
},
"response": {
"status": request.response.status_code,
"headers": [{"name": k, "value": v} for k, v in request.response.headers.items()],
},
}
har["log"]["entries"].append(entry)
with open(filename, "w") as f:
json.dump(har, f, indent=2)
print(f"HAR exported: {filename} ({len(har['log']['entries'])} entries)")
HAR dosyaları çerez ve başlık taşır; paylaşmadan önce hassas alanları temizleyin, saklama süresi belirleyin.
Eşzamanlılık ve maliyet: kaç thread gerekiyor?
CaptchaAI thread tabanlı çalışır: ödediğiniz şey eşzamanlı çözüm kapasitesidir, çözüm başına ücret yoktur. Bir thread, o an işlenen tek bir CAPTCHA demektir.
- BASIC ($15/ay, 5 thread) — tek CI hattı ve gece koşan birkaç senaryo için yeterli.
- ADVANCE ($90/ay, 50 thread) — paralel test filoları ve düzenli veri kazıma işleri bu bantta rahat eder.
Kaba hesabı çözüm sürelerinden yapın: reCAPTCHA v2 60 saniyenin, Cloudflare Turnstile 10 saniyenin, GeeTest v3 ise 12 saniyenin altında tamamlanır. Kuyrukta bekleyen görev sayısı sürekli artıyorsa darboğaz thread sayısıdır. Kur dalgalanmasının bütçeyi zorladığı bir pazarda aylık sabit USD tutarı planlamayı kolaylaştırır.
Kapsam konusunda net olun: CaptchaAI reCAPTCHA v2 ve v3 (Enterprise sürümleri dahil), Cloudflare Turnstile ve Cloudflare doğrulama akışı, GeeTest v3, görüntü/OCR, grid ve BLS türlerini çözer. CaptchaFox (beta), Friendly Captcha (beta) ve Lemin (beta) beta aşamasındadır. hCaptcha ve FunCaptcha (Arkose Labs) desteklenmiyor; GeeTest v4 için yalnızca "çok yakında" ifadesi geçerlidir.
Sorun giderme
| Sorun | Olası neden | Çözüm |
|---|---|---|
seleniumwire import hatası |
Paket kurulu değil | pip install seleniumwire çalıştırın |
driver.requests boş dönüyor |
Liste, istekler tamamlanmadan sorgulandı | Gezinmeden sonra kısa bir bekleme ekleyin |
| Proxy üzerinden SSL hatası | Sertifika zinciri doğrulanamıyor | seleniumwire_options['verify_ssl'] = False ayarlayın |
| Bellek kullanımı sürekli artıyor | İstek geçmişi birikiyor | Periyodik olarak del driver.requests çağırın |
| Sayfalar yavaş açılıyor | Tüm trafik proxy üzerinden geçiyor | exclude_hosts ile statik alan adlarını kapsam dışında bırakın |
| CAPTCHA tespit edilemiyor | Widget iframe içinde yükleniyor | iframe URL'leri için driver.requests listesini tarayın |
Sık sorulan sorular
Ağ trafiğinden hangi CAPTCHA türlerini tanıyabilirim?
Ayırt edici bir ağ imzası olan her türü. reCAPTCHA v2 api2/anchor, reCAPTCHA v3 render= parametresi, Cloudflare Turnstile ise challenges.cloudflare.com isteğiyle kendini belli eder. Yukarıdaki sınıf bu üçünü tanıyor; GeeTest v3 için de aynı desene kendi kontrolünüzü ekleyebilirsiniz.
Token'ı enjekte ettim ama form yine reddediyor, neden?
Birincisi, pageurl değeri tarayıcının o an bulunduğu adresle birebir aynı olmalıdır; sondaki eğik çizgi farkı bile yeter. İkincisi, token kısa ömürlüdür — enjeksiyondan sonra formu bekletmeden gönderin. Üçüncüsü, callback bekleyen widget'larda yalnızca gizli alanı doldurmak yetmez; sayfanın kendi doğrulama fonksiyonunu da tetiklemeniz gerekir.
Uzun süren oturumlarda bellek neden şişiyor?
driver.requests listesi sürücü açık kaldığı sürece büyür ve yanıt gövdelerini de tutar. Saatlerce çalışan işlerde her senaryo sonunda del driver.requests çağırın, sürücüyü belirli aralıklarla yeniden başlatın.
KVKK açısından nelere dikkat etmeliyim?
Yakalanan trafik ve dışa aktardığınız HAR dosyaları kişisel veri içerebilir: test kullanıcısının e-postası, oturum başlıkları, form alanları. Bu dosyaları paylaşılan bir depoya atmadan önce maskeleyin, erişimi ekiple sınırlayın ve saklama süresi tanımlayın. Testlerde gerçek müşteri verisi yerine üretilmiş tr-TR verisi kullanın.
İlgili kılavuzlar
- Python Selenium + CaptchaAI tam entegrasyon rehberi
- Chrome DevTools Protocol ile CAPTCHA otomasyonu
- Proxy kalitesinin CAPTCHA çözüm oranına etkisi
Ağ trafiğini okumaya bugün başlayın — CaptchaAI anahtarınızı alın ve ilk token'ı Selenium Wire ile forma yazın.