Scrapy captcha middleware: spider'ınızda captcha çözme (2026)
Scrapy istekler ve callback'ler etrafında kurulmuştur, bu yüzden bir captcha ona düzgün oturmaz. Bunu temiz şekilde ele almanın yolu bir downloader middleware'dir: her yanıtı görür, bir challenge fark eder, bir token alır ve spider'ın formu o token ile göndermesine izin verir. Bu rehber, Scarnox'un bugün proxysiz olarak çözdüğü PopularCaptcha sayfaları için böyle bir middleware kurar.
Ücretsiz deneme kredisiyle başlayın, kart gerekmez ve kodu, başka bir yere yönlendirmeden önce bir test sayfasında deneyin.
Middleware nasıl çalışır?
Middleware her yanıtta çalışır:
- Yanıtta
data-sitekeyyoksa, yanıtı olduğu gibi geçirir. - Varsa, middleware captcha'yı çözer ve aynı isteğin bir kopyasını, token
metaiçinde saklanmış halde döndürür. - Scrapy o isteği yeniden çeker. Middleware token'ı
metaiçinde görür ve yanıtı geçirir. - Spider token'ı
meta'dan okur ve formuFormRequest.from_responseile gönderir.
Çözüm çağrısı deferToThread ile bir thread'de çalışır, bu yüzden Scrapy'nin reactor'ını bloklamaz. Captcha çözülürken diğer istekler ilerlemeye devam eder.
Proje ayarları
Anahtarınızı koda değil, bir ortam değişkenine koyun:
# settings.py
import os
SCARNOX_API_KEY = os.environ["SCARNOX_API_KEY"]
SCARNOX_TIMEOUT = 185
DOWNLOADER_MIDDLEWARES = {
"myproject.middlewares.PopularCaptchaMiddleware": 543,
}
ROBOTSTXT_OBEY = True
DOWNLOAD_DELAY = 3
CONCURRENT_REQUESTS_PER_DOMAIN = 1
Download gecikmesi ve domain başına tek eşzamanlılık bilerek konmuştur. Sitenin yükünü düşük tutar ve gördüğünüz challenge sayısını azaltır.
Middleware
# myproject/middlewares.py
import requests
from scrapy import Request
from scrapy.http import Response
from twisted.internet.threads import deferToThread
API_URL = "https://api.scarnox.com/api/tasks/create"
class PopularCaptchaMiddleware:
def __init__(self, api_key: str, timeout: int):
self.api_key = api_key
self.timeout = timeout
@classmethod
def from_crawler(cls, crawler):
return cls(
api_key=crawler.settings.get("SCARNOX_API_KEY"),
timeout=crawler.settings.getint("SCARNOX_TIMEOUT", 185),
)
def _solve(self, sitekey: str, page_url: str) -> str:
r = requests.post(
API_URL,
headers={"Authorization": f"Bearer {self.api_key}"},
json={
"captcha_type": "PopularCaptchaTaskProxyless",
"site_key": sitekey,
"site_url": page_url,
},
timeout=self.timeout, # bir worker thread'de çalışır, reactor'da değil
)
data = r.json()
if data.get("errorId") != 0:
raise RuntimeError(f"{data.get('errorCode')}: {data.get('errorDescription')}")
return data["solution"]["token"]
def process_response(self, request: Request, response: Response, spider):
# Zaten çözülmüş ya da kendi oluşturduğumuz bir form gönderimi: geçir.
if request.meta.get("captcha_token") or request.meta.get("skip_captcha"):
return response
sitekey = response.css("[data-sitekey]::attr(data-sitekey)").get()
if not sitekey:
return response
spider.logger.info("PopularCaptcha on %s, solving", response.url)
def retry_with_token(token: str) -> Request:
# Bir Request döndürmek onu yeniden zamanlar. dont_filter, dupe filtresini atlar.
return request.replace(
dont_filter=True,
meta={**request.meta, "captcha_token": token},
)
return deferToThread(self._solve, sitekey, response.url).addCallback(retry_with_token)
Scrapy, process_response içinden bir Deferred döndürmeyi destekler. Callback token'ı yeniden denenen bir isteğe çevirir ve Scrapy onu diğerleri gibi zamanlar.
Spider
# myproject/spiders/search.py
import scrapy
from scrapy.http import FormRequest
class SearchSpider(scrapy.Spider):
name = "search"
start_urls = ["https://search.example.com/search?q=web+scraping"]
def parse(self, response):
token = response.meta.get("captcha_token")
if token and response.css("[data-sitekey]"):
# Scrapy formun kendi alanlarını doldurur; biz token'ı ekliyoruz.
yield FormRequest.from_response(
response,
formdata={
"h-captcha-response": token,
"g-recaptcha-response": token,
},
callback=self.parse_results,
meta={"skip_captcha": True},
dont_filter=True,
)
return
yield from self.parse_results(response)
def parse_results(self, response):
for item in response.css("div.result"):
yield {
"title": item.css("h3::text").get(),
"url": item.css("a::attr(href)").get(),
}
Seçiciler yer tutucudur. Kazıdığınız siteden gerçek olanlarla değiştirin.
skip_captcha bayrağı, middleware'in form gönderimini yeniden çözmesini engeller. Bu bayrak olmadan her gönderim bir çözüm daha maliyeti olurdu.
Çalıştırın
export SCARNOX_API_KEY=scarnox_YOURAPIKEY
scrapy crawl search -O results.json
Logu izleyin. PopularCaptcha on ..., solving gibi bir satır, bir challenge bulunduğunu ve bir token istendiğini gösterir. Bu satırı her sayfada görüyorsanız yavaşlayın. Sık challenge'lar, sitenin taramayı fark ettiğinin işaretidir.
Token zamanlaması
Bir PopularCaptcha token'ı yaklaşık iki dakika içinde sona erer. Middleware token geldiği anda yeniden deneme isteğini talep eder ve spider aynı callback zincirinde gönderir. Zinciri kısa tutun. Token ile form gönderimi arasına yavaş indirmeler ya da uzun işlemler eklemeyin. Bu süre hakkında daha fazlası için captcha token süresi sayfasına bakın.
Hatalar
Başarısız bir çözüm, thread içinde RuntimeError fırlatır. Scrapy bunu başarısız bir istek olarak loglar. Başarısız çözümler ücretlendirilmez, bu yüzden yeniden denemek ucuzdur. Sınırları olan bir yeniden deneme politikası için Scrapy'nin yerleşik RetryMiddleware bileşenini kullanın ve _solve'u kısa bir döngüye sarın. Üretimde işe yarayan kalıplar için captcha çözücü hata yönetimi rehberimize bakın.
Sorumlu scraping
- robots.txt'ye uyun. Yukarıdaki
ROBOTSTXT_OBEY = Truebunu sağlar. Açık tutun. - Gecikmeyi yüksek tutun. Bir challenge, sitenin sizden yavaşlamanızı istediğinin işaretidir. Daha hızlı bitirmek için gecikmeyi aşmayın.
- Kullanım koşullarını okuyun. Tarama yapmadan önce bazı siteler otomatik erişimi tamamen yasaklar.
- Kişisel veriyi dikkatle işleyin. Yalnızca ihtiyacınız olan alanları saklayın ve sakladıklarınızı koruyun.
Hukuki arka plan için web scraping yasal mı sayfasına bakın.
Fiyatlandırma
Scarnox başarılı çözüm başına ücretlendirir; başarısız çözümler ücretsizdir. Oranlar için güncel fiyatlandırma sayfasına bakın. Ücretsiz deneme kredisiyle başlayın, kart gerekmez.