Bir Scrapy taraması, hedef sayfa reCAPTCHA v2 veya görüntü tabanlı bir CAPTCHA döndürdüğü anda durur — ve ürün verisi yerine bir doğrulama sayfası toplarsınız. Bunu çözmenin temiz yolu, çözümü doğrudan tarama akışının içine gömen bir downloader middleware'dir: sayfadaki sitekey'i yakalar, CaptchaAI API'ye gönderir, dönen token'ı forma ekleyip isteği yeniden yürütür. Bu kılavuz o middleware'i sıfırdan kurar; mevcut spider kodunuza dokunmanıza gerek kalmaz.
Nasıl çalışır: tarama akışının içinde çözüm
Akış dört adımdan oluşur ve tamamı middleware katmanında yaşar:
- Tespit — middleware, gelen HTML yanıtında
data-sitekeyözniteliğini arar. - Gönderim — bir sitekey bulursa, sayfa URL'siyle birlikte CaptchaAI'nin
in.phpuç noktasına bir görev gönderir ve bir görev kimliği alır. - Sorgulama —
res.phpuç noktasını birkaç saniyede bir sorgular (periyodik sorgulama) ve çözümün hazır olmasını bekler. - Enjeksiyon — dönen token'ı
request.metaüzerine yazar; spider bu token'ıg-recaptcha-responsealanıyla forma ekleyip sayfayı yeniden gönderir.
Örümcek mantığınızın bu ayrıntıların hiçbirini bilmesi gerekmez — middleware tüm işi arka planda yapar, siz yalnızca ayrıştırma kurallarınıza odaklanırsınız.
Başlamadan önce: gereksinimler
Kuruluma başlamadan önce ortamınızda şunların bulunması gerekir. Sürüm numaralarını tablodaki gibi (nokta ile) bırakın; bunlar kod düzeyi değerlerdir.
| Gereksinim | Ayrıntılar |
|---|---|
| Python | 3.8+ |
| Scrapy | 2.5+ |
| requests | CaptchaAI API çağrıları için |
| CaptchaAI API anahtarı | ücretsiz hesabınızdan alın |
pip install scrapy requests
CaptchaAI çözücü sınıfı
İlk olarak, CaptchaAI API'sini saran küçük bir çözücü sınıfı yazın. Scrapy proje kökünüzde captcha_solver.py dosyasını oluşturun. Sınıf iki metot sunar:
solve_recaptcha— göreviuserrecaptchayöntemiyle gönderir ve token hazır olana kadarres.php'yi sorgular.solve_image— base64 kodlu görüntü CAPTCHA'ları için aynı gönder-sorgula desenini izler.
Varsayılan zaman aşımı reCAPTCHA için 300 saniye, görüntü için 120 saniyedir — yavaş ağlarda bu değerleri artırabilirsiniz.
import requests
import time
class CaptchaAISolver:
def __init__(self, api_key):
self.api_key = api_key
self.base_url = "https://ocr.captchaai.com"
def solve_recaptcha(self, site_key, page_url, timeout=300):
resp = requests.get(f"{self.base_url}/in.php", params={
"key": self.api_key,
"method": "userrecaptcha",
"googlekey": site_key,
"pageurl": page_url,
})
if not resp.text.startswith("OK|"):
raise Exception(f"Submit failed: {resp.text}")
task_id = resp.text.split("|")[1]
deadline = time.time() + timeout
while time.time() < deadline:
time.sleep(5)
result = requests.get(f"{self.base_url}/res.php", params={
"key": self.api_key,
"action": "get",
"id": task_id,
})
if result.text == "CAPCHA_NOT_READY":
continue
if result.text.startswith("OK|"):
return result.text.split("|", 1)[1]
raise Exception(f"Solve failed: {result.text}")
raise TimeoutError(f"Task {task_id} timed out")
def solve_image(self, image_base64, timeout=120):
resp = requests.get(f"{self.base_url}/in.php", params={
"key": self.api_key,
"method": "base64",
"body": image_base64,
})
if not resp.text.startswith("OK|"):
raise Exception(f"Submit failed: {resp.text}")
task_id = resp.text.split("|")[1]
deadline = time.time() + timeout
while time.time() < deadline:
time.sleep(5)
result = requests.get(f"{self.base_url}/res.php", params={
"key": self.api_key,
"action": "get",
"id": task_id,
})
if result.text == "CAPCHA_NOT_READY":
continue
if result.text.startswith("OK|"):
return result.text.split("|", 1)[1]
raise Exception(f"Solve failed: {result.text}")
raise TimeoutError(f"Task {task_id} timed out")
CAPTCHA'yı yakalayan Scrapy middleware'i
Çözücü hazır olduğuna göre, onu her yanıtta otomatik çalışan bir Scrapy downloader middleware'ine (ara yazılım) bağlayın. middlewares.py dosyasını oluşturun. process_response metodu her yanıtı iki denetimden geçirir:
- reCAPTCHA —
data-sitekeyözniteliğini regex ile arar ve bulursasolve_recaptchaçağırır. - Görüntü CAPTCHA'sı —
img#captcha-imageöğesindeki base64 veriyi çıkarır vesolve_imageçağırır.
Her iki durumda da sonuç request.meta içine yazılır, böylece spider ona kolayca erişir.
import base64
import re
from scrapy import signals
from scrapy.http import HtmlResponse
from captcha_solver import CaptchaAISolver
class CaptchaAIMiddleware:
"""Scrapy downloader middleware that detects and solves CAPTCHAs."""
def __init__(self, api_key):
self.solver = CaptchaAISolver(api_key)
@classmethod
def from_crawler(cls, crawler):
api_key = crawler.settings.get("CAPTCHAAI_API_KEY")
if not api_key:
raise ValueError("CAPTCHAAI_API_KEY setting is required")
return cls(api_key)
def process_response(self, request, response, spider):
# Check for reCAPTCHA on the page
site_key = self._find_recaptcha_key(response.text)
if site_key:
spider.logger.info(f"reCAPTCHA detected on {response.url}")
token = self.solver.solve_recaptcha(site_key, response.url)
request.meta["captcha_token"] = token
spider.logger.info("CAPTCHA solved successfully")
# Check for image CAPTCHA
captcha_img = self._find_image_captcha(response)
if captcha_img:
spider.logger.info(f"Image CAPTCHA detected on {response.url}")
text = self.solver.solve_image(captcha_img)
request.meta["captcha_text"] = text
spider.logger.info(f"Image CAPTCHA solved: {text}")
return response
def _find_recaptcha_key(self, html):
match = re.search(
r'data-sitekey=["\']([A-Za-z0-9_-]+)["\']', html
)
return match.group(1) if match else None
def _find_image_captcha(self, response):
img = response.css("img#captcha-image::attr(src)").get()
if img and img.startswith("data:image"):
return img.split(",", 1)[1]
return None
settings.py yapılandırması
Middleware'i etkinleştirmek için settings.py dosyasına ekleyin. 560 öncelik değeri, middleware'i indirici zincirinde yanıt geldikten sonra çalışacak konuma yerleştirir. API anahtarınızı koda gömmeyin; bir ortam değişkeninden okuyun.
import os
CAPTCHAAI_API_KEY = os.environ.get("CAPTCHAAI_API_KEY")
DOWNLOADER_MIDDLEWARES = {
"myproject.middlewares.CaptchaAIMiddleware": 560,
}
Örnek spider: token'ı forma enjekte etmek
Örnek bir spider, token'ın nasıl kullanılacağını gösterir. request.meta içinde bir captcha_token varsa, spider sayfayı g-recaptcha-response alanıyla bir FormRequest olarak yeniden gönderir; yoksa doğrudan ayrıştırmaya geçer. Kendi projenizde example.com yerine yalnızca yetkili olduğunuz hedef URL'yi kullanın.
import scrapy
class ProductSpider(scrapy.Spider):
name = "products"
start_urls = ["https://example.com/products"]
def parse(self, response):
# If CAPTCHA was solved, the token is in meta
token = response.meta.get("captcha_token")
if token:
# Resubmit the page with the token
yield scrapy.FormRequest(
url=response.url,
formdata={"g-recaptcha-response": token},
callback=self.parse_products,
)
else:
yield from self.parse_products(response)
def parse_products(self, response):
for product in response.css(".product-item"):
yield {
"name": product.css("h2::text").get(),
"price": product.css(".price::text").get(),
"url": response.urljoin(
product.css("a::attr(href)").get()
),
}
next_page = response.css("a.next-page::attr(href)").get()
if next_page:
yield scrapy.Request(response.urljoin(next_page))
CAPTCHA sayfalarında otomatik yeniden deneme
Bazı siteler geçersiz oturumlarda ürün içeriği yerine tam sayfa CAPTCHA döndürür. Bu durumları yakalamak için basit bir yeniden deneme middleware'i ekleyin. Yanıt gövdesinde g-recaptcha veya cf-turnstile gibi göstergeler varsa isteği en fazla üç kez yeniden kuyruğa alır.
class CaptchaRetryMiddleware:
"""Retry requests that return CAPTCHA challenge pages."""
max_retries = 3
def process_response(self, request, response, spider):
if self._is_captcha_page(response):
retries = request.meta.get("captcha_retries", 0)
if retries < self.max_retries:
request.meta["captcha_retries"] = retries + 1
spider.logger.info(
f"CAPTCHA page detected, retry {retries + 1}"
)
return request.copy()
return response
def _is_captcha_page(self, response):
indicators = [
"g-recaptcha",
"cf-turnstile",
"captcha-image",
"Please verify you are human",
]
return any(ind in response.text for ind in indicators)
Spider'ı çalıştırın
Son olarak API anahtarınızı bir ortam değişkeni olarak dışa aktarın ve taramayı başlatın.
export CAPTCHAAI_API_KEY="YOUR_API_KEY"
scrapy crawl products -o products.json
Eşzamanlılık ve thread planlaması
CaptchaAI thread bazlı faturalandırılır: her thread aynı anda tek bir CAPTCHA çözer ve çözüm biter bitmez sıradaki göreve geçer. Çözüm başına ücret ya da günlük kota yoktur. Bu, Scrapy'nin CONCURRENT_REQUESTS ayarıyla doğrudan ilişkilidir — aynı anda çok sayıda CAPTCHA'lı sayfa işlemeyi planlıyorsanız, plan thread sayınız bu eşzamanlılığı karşılamalıdır.
Tarama hacminize göre bir başlangıç noktası:
- BASIC ($15/ay, 5 thread) — küçük tarama işleri ve düşük eşzamanlılık için çoğu zaman yeterlidir.
- STANDARD ($30/ay, 15 thread) — orta ölçekli, düzenli taramalar.
- ADVANCE ($90/ay, 50 thread) — yoğun, paralel taramalar.
Fiyatlar USD üzerinden sabittir; TL kurundaki dalgalanmadan etkilenmeyen öngörülebilir aylık maliyet, Türkiye'deki otomasyon ekipleri için pratik bir avantajdır. reCAPTCHA v2 çözümleri genellikle sayfa başına 5–15 saniye sürdüğünden, CONCURRENT_REQUESTS değerini makul tutarak CAPTCHA'sız sayfaları beklemeye takılmadan taramaya devam edebilirsiniz.
Yetkili kullanım ve KVKK notu
Otomatik veri kazıma her zaman kaynak sitenin kullanım şartları ve ilgili mevzuat çerçevesinde yapılmalıdır. Üretime almadan önce şu üç noktayı gözden geçirin:
- Türkiye'de topladığınız veriler kişisel veri içeriyorsa KVKK (Kişisel Verilerin Korunması Kanunu) kapsamına girer.
- CaptchaAI'yi yalnızca yetkili olduğunuz QA ve veri toplama iş akışlarında kullanın.
- Örneklerdeki
example.comgibi yer tutucu adresleri kendi izinli hedeflerinizle değiştirin.
Bu çerçeveyi baştan kurmak, hem yasal hem operasyonel açıdan taramanızı sürdürülebilir kılar.
Sorun giderme
| Sorun | Sebep | Düzeltme |
|---|---|---|
ValueError: CAPTCHAAI_API_KEY setting is required |
Ortam değişkeni tanımlı değil | CAPTCHAAI_API_KEY değişkenini ayarlayın |
| CAPTCHA algılanmıyor | Sayfanın HTML yapısı farklı | Middleware'deki data-sitekey regex desenini güncelleyin |
Çözüm sırasında TimeoutError |
Yavaş çözüm veya ağ gecikmesi | Çözücüdeki zaman aşımı değerini artırın |
| Spider çözümden sonra engelleniyor | IP tabanlı engelleme | Genel bir proxy yapılandırması ekleyin |
Sık sorulan sorular
Scrapy'nin CONCURRENT_REQUESTS ayarı ile thread sayısı nasıl eşleşir?
Her eşzamanlı CAPTCHA çözümü bir thread kullanır. Aynı anda çok sayıda CAPTCHA'lı sayfa işliyorsanız, CONCURRENT_REQUESTS değerini plan thread sayınızı aşmayacak şekilde ayarlayın; aksi halde istekler kuyruğa girer ve bekler.
CaptchaAI hangi CAPTCHA türlerini çözer?
reCAPTCHA v2 ve v3, Cloudflare Turnstile, GeeTest v3, görüntü/OCR, grid ve BLS CAPTCHA'ları genel kullanımda desteklenir; CaptchaFox, Friendly Captcha ve Lemin ise beta aşamasındadır. hCaptcha ve FunCaptcha (Arkose Labs) şu anda desteklenmez, GeeTest v4 için destek çok yakında. Bu middleware'i Turnstile veya GeeTest v3 için genişletmek isterseniz process_response içine ilgili türü denetleyen ek koşullar ekleyin.
reCAPTCHA v2 çözümü taramamı ne kadar yavaşlatır?
Çözüm genellikle sayfa başına 5–15 saniye sürer ve yalnızca CAPTCHA içeren sayfaları etkiler. CONCURRENT_REQUESTS sayesinde bir sayfanın çözümünü beklerken diğer sayfaları taramaya devam edebilirsiniz.
Token geçerli ama site yine de engelliyor, ne yapmalıyım?
Bu genellikle CAPTCHA'dan bağımsız, IP tabanlı bir engellemedir. Gerçekçi HTTP başlıkları gönderin, istekler arasına gecikme koyun ve gerekirse genel bir proxy yapılandırması ekleyin.
Scrapy-Splash veya Scrapy-Playwright ile çalışır mı?
Evet. JavaScript ile oluşturulan sayfalarda middleware aynı şekilde çalışır; nihai HTML yanıtını CAPTCHA öğeleri için denetler.