Scarnox · Blog

Arama sonuçlarını captcha'yı geçerek scrape etme (Python rehberi, 2026)

Arama sonuç sayfaları, bir scraper'ın captcha ile ilk karşılaştığı yerlerden biridir. Site tek bir adresten bir istek patlaması görür, bir challenge sunar ve challenge geçilene kadar sonuç döndürmez. Arama sonuçlarını captcha'yı geçerek scrape etmek için betiğinizin üç adıma ihtiyacı vardır: ayrıştırmadan önce challenge'ı tespit etmek, tek bir API çağrısıyla çözmek ve token'ı göndererek sitenin gerçek sonuç sayfasını döndürmesini sağlamak.

Bu rehber bu akışı requests ve BeautifulSoup ile Python'da gösterir. Bir PopularCaptcha widget'ı gösteren sayfaları kapsar; Scarnox'un bugün çözdüğü tip budur (proxysiz). Ücretsiz deneme kredisiyle başlayın, kart gerekmez ve kodu önce kendi sahip olduğunuz bir sayfada çalıştırın.

Challenge nasıl görünür?

Arama sayfaları widget'ı genellikle bir formun içine koyar. Widget kapsayıcısı bir data-sitekey özniteliği taşır ve form siteye geri gönderir:

<form action="/search/verify" method="post">
  <input type="hidden" name="q" value="web scraping">
  <input type="hidden" name="lang" value="en">
  <div class="h-captcha" data-sitekey="YOUR-SITEKEY-UUID"></div>
  <button type="submit">Continue</button>
</form>

Betiğinizin bu işaretlemeden üç şeye ihtiyacı vardır: sitekey, formun action URL'si ve token ile birlikte geri gönderilmesi gereken gizli alanlar. Aşağıdaki yardımcı fonksiyon üçünü de okur; bu yüzden hiçbirini sabit kodlamanız gerekmez.

Hedefinizdeki widget farklı bir captcha tipiyse, captcha tipleri sayfasına bakın. reCAPTCHA ve Cloudflare Turnstile desteği yakında geliyor; bu yüzden buradaki kod yalnızca PopularCaptcha'yı kapsar.

Çözücü çağrısı

Çözüm isteği, anahtarınızı Bearer başlığında taşıyarak Scarnox API'sine gider. Çağrı token hazır olana kadar bloklanır, bu yüzden bir yoklama döngüsü yoktur:

import time
from urllib.parse import urljoin

import requests
from bs4 import BeautifulSoup

API_KEY = "scarnox_YOURAPIKEY"
SEARCH_URL = "https://search.example.com/search"

session = requests.Session()
session.headers["User-Agent"] = (
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
    "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36"
)


def solve(sitekey: str, page_url: str) -> str:
    r = requests.post(
        "https://api.scarnox.com/api/tasks/create",
        headers={"Authorization": f"Bearer {API_KEY}"},
        json={
            "captcha_type": "PopularCaptchaTaskProxyless",
            "site_key": sitekey,
            "site_url": page_url,
        },
        timeout=185,  # çağrı token hazır olduğunda döner
    )
    data = r.json()
    if data.get("errorId") != 0:
        raise RuntimeError(f"{data.get('errorCode')}: {data.get('errorDescription')}")
    return data["solution"]["token"]

Başarısız bir çözüm, errorId için 0 dışında bir değer döndürür ve bunun için ücret alınmaz. Yeniden denemeler captcha çözücü hata yönetimi rehberinde ele alınır.

Sayfayı çekin ve captcha'yı geçin

Getirme fonksiyonu önce widget'ı kontrol eder. Yoksa HTML'i değiştirmeden döndürür. Varsa, challenge'ı çözer, formu aynı gizli alanlarla yeniden oluşturur ve gönderir:

def fetch_results(query: str, page: int = 1) -> str:
    resp = session.get(SEARCH_URL, params={"q": query, "page": page}, timeout=30)
    soup = BeautifulSoup(resp.text, "html.parser")

    widget = soup.select_one("[data-sitekey]")
    if widget is None:
        return resp.text  # bu sayfada challenge yok

    form = widget.find_parent("form")
    if form is None:
        raise RuntimeError("Captcha widget'ı bir formun içinde değil")

    token = solve(widget["data-sitekey"], resp.url)

    payload = {
        field["name"]: field.get("value", "")
        for field in form.select("input[name]")
    }
    payload["h-captcha-response"] = token
    payload["g-recaptcha-response"] = token

    action = urljoin(resp.url, form.get("action") or resp.url)
    verified = session.post(action, data=payload, timeout=30)

    if "data-sitekey" in verified.text:
        raise RuntimeError("Gönderimden sonra challenge hâlâ gösteriliyor; token reddedilmiş olabilir")
    return verified.text

Burada iki ayrıntı önemlidir. site_url, çapraz arama uç noktası değil, captcha'nın gerçekten sunulduğu URL'dir (resp.url; yönlendirmeler dahil). Ayrıca oturum, GET ile POST arasında çerezleri tutar; birçok site token'ı kabul etmek için bunu ister.

Token'lar yaklaşık iki dakika içinde sona erer; çözüm bu yüzden formu okuduktan hemen sonra yapılır, önceden değil. Bu süre hakkında daha fazlası için captcha token süresi sayfasına bakın.

Sonuçları ayrıştırın

Gerçek sonuç sayfası döndüğünde, ayrıştırma sıradan HTML işidir. Aşağıdaki seçiciler yer tutucudur; sayfayı DevTools'ta açıp gerçek seçicilerle değiştirin:

def parse_results(html: str) -> list[dict]:
    soup = BeautifulSoup(html, "html.parser")
    rows = []
    for item in soup.select("div.result"):
        link = item.select_one("a[href]")
        heading = item.select_one("h3")
        if link and heading:
            rows.append({
                "title": heading.get_text(strip=True),
                "url": link["href"],
            })
    return rows


if __name__ == "__main__":
    for page in range(1, 4):
        html = fetch_results("web scraping", page)
        for row in parse_results(html):
            print(row)
        time.sleep(5)  # sayfalar arasında istekleri yavaşlatın

Her sayfanın çıktısını ayrı tutun. Bir sayfa sıfır satır döndürürse, sorgunun sonucu olmadığını varsaymadan önce widget'ı kontrol edin.

Sorumlu scraping

Captcha bir boşluk değil, bir sinyaldir. Bu betiği herhangi bir arama sitesine çalıştırmadan önce:

Yasal taraf için web scraping yasal mı sayfasına bakın. Baştan blokları önlemek için engellenmeden scraping 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.

Sonraki adımlar