Use Cases

Eczane Fiyat Karşılaştırması için CAPTCHA Kullanımı

Birden fazla eczane portalından ilaç fiyatı toplayan bir karşılaştırma aracının önündeki asıl engel genellikle CAPTCHA'dır: arama formları reCAPTCHA v2, sonuç sayfaları ise sık sık Cloudflare Turnstile ister. Çözüm nettir — doğrulamayı API üzerinden çözün, dönen token'ı forma enjekte edin, oturumu koruyarak tekrar sorgulayın. Bu rehber bu üç adımı tek bir Python iş akışında birleştirmeyi ve kaynakları paralel karşılaştırmayı gösterir.

Çözüm mimarisini dört katmanda kurun

Eczane fiyatı toplayan bir aracı sağlam tutmanın yolu, işi net katmanlara ayırmaktır. Her katmanı ayrı düşünürseniz bir portal doğrulama türünü değiştirdiğinde yalnızca ilgili katmanı güncellersiniz, çekirdek akış aynı kalır:

  1. Kaynak envanteri: Hangi eczane portalını sorgulayacağınızı ve her birinin hangi doğrulamayı gösterdiğini tek bir listede tutun.
  2. Doğrulama katmanı: reCAPTCHA v2, Turnstile veya Image CAPTCHA'yı CaptchaAI API ile çözüp token'ı alın.
  3. Oturum katmanı: Çerezleri koruyan tek bir oturumda kalın; böylece her sorguda yeniden doğrulama tetiklenmez.
  4. Toplama katmanı: Dönen fiyatları normalize edip en düşük değeri çıkarın.

Bu ayrım, aşağıdaki Python ve Node.js örneklerinin neden bu sırayla ilerlediğini de açıklar: önce türü bilirsiniz, sonra çözersiniz, en sonda tek oturumda toplarsınız.

CAPTCHA'lar eczane portallarında nerede çıkar?

Doğru çözüm yöntemini seçmek için önce hangi sayfanın hangi doğrulamayı tetiklediğini bilmeniz gerekir:

Sayfa türü Sık görülen CAPTCHA Tetikleyici
İlaç arama formu reCAPTCHA v2 Her arama sorgusu
Fiyat sonuç sayfası Cloudflare Turnstile Otomatik erişim şüphesi
Eczane bulucu reCAPTCHA v2 Konum tabanlı sorgular
Kupon arama Image CAPTCHA İndirim kodları gösterilmeden önce
Sigorta formülleri reCAPTCHA v2 Giriş/arama kapıları

CaptchaAI bu türlerin üçünü de (reCAPTCHA v2, Cloudflare Turnstile ve OCR tabanlı Image CAPTCHA) aynı API üzerinden çözer; bir portal sağlayıcısını değiştirse bile çözüm mantığınız aynı kalır.

Temel iş akışı: reCAPTCHA v2 çözümü

Çekirdek desen üç işlemdir: sayfadan sitekey'i çıkarın, doğrulamayı in.php ile gönderip res.php'den sorgulayın, dönen token'ı forma ekleyin.

import requests
import time

session = requests.Session()
session.headers.update({
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
})

def solve_recaptcha(site_key, page_url):
    resp = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": "YOUR_API_KEY",
        "method": "userrecaptcha",
        "googlekey": site_key,
        "pageurl": page_url,
        "json": 1
    })
    task_id = resp.json()["request"]

    for _ in range(60):
        time.sleep(3)
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": "YOUR_API_KEY",
            "action": "get",
            "id": task_id,
            "json": 1
        })
        data = result.json()
        if data["status"] == 1:
            return data["request"]
    raise TimeoutError("Solve timed out")

def search_drug_prices(drug_name, zipcode):
    search_url = "https://pharmacy.example.com/search"

    # Load the search page to get the site key
    page = session.get(search_url)

    # Extract reCAPTCHA site key
    import re
    match = re.search(r'data-sitekey="([^"]+)"', page.text)
    site_key = match.group(1)

    # Solve the CAPTCHA
    token = solve_recaptcha(site_key, search_url)

    # Submit the search with token
    results = session.post(search_url, data={
        "drug": drug_name,
        "zip": zipcode,
        "g-recaptcha-response": token
    })

    return parse_prices(results.text)

Token, reCAPTCHA v2 için her zaman g-recaptcha-response alanına yazılır ve alındıktan sonra dakikalar içinde geçerliliğini yitirir; bu yüzden çözüm ile form gönderimini arka arkaya tutun.

İpucu: Çözüm çağrısı ile form POST'unu aynı fonksiyonda tutun; ikisi arasına ağ gecikmesi dışında hiçbir bekleme koymayın. Token'ın süresi dolarsa portal isteği sessizce reddedip sizi yeni bir doğrulamaya düşürür.

Birden fazla eczaneyi karşılaştırma

Aracın değeri, aynı ilacı birkaç kaynaktan sorgulayıp en düşük fiyatı çıkarmaktır. Her kaynağın CAPTCHA türünü bir yapılandırma listesinde tutmak çözüm mantığını temiz bırakır.

Not: PHARMACY_SOURCES listesini tek doğruluk kaynağı yapın. Yeni bir eczane eklerken yalnızca bu listeye bir satır girin; çözüm ve toplama katmanları değişmeden çalışır.

PHARMACY_SOURCES = [
    {
        "name": "PharmacyA",
        "url": "https://pharmacya.example.com/pricing",
        "captcha_type": "recaptcha_v2"
    },
    {
        "name": "PharmacyB",
        "url": "https://pharmacyb.example.com/drugs",
        "captcha_type": "turnstile"
    },
    {
        "name": "PharmacyC",
        "url": "https://pharmacyc.example.com/search",
        "captcha_type": "image"
    }
]

def compare_drug_price(drug_name, zipcode):
    results = []

    for source in PHARMACY_SOURCES:
        try:
            prices = fetch_prices(
                source["url"],
                source["captcha_type"],
                drug_name,
                zipcode
            )
            results.append({
                "source": source["name"],
                "prices": prices,
                "status": "success"
            })
        except Exception as e:
            results.append({
                "source": source["name"],
                "error": str(e),
                "status": "failed"
            })

    return sorted(results, key=lambda r: r.get("prices", {}).get("lowest", float("inf")))

Farklı CAPTCHA türlerini tek fonksiyonda ele alma

Tür kontrolünü tek bir fetch_prices fonksiyonunda toplamak bakımı kolaylaştırır. Her dalda doğru token alanının (g-recaptcha-response, cf-turnstile-response veya captcha) seçildiğine dikkat edin.

def fetch_prices(url, captcha_type, drug_name, zipcode):
    page = session.get(url)

    if captcha_type == "recaptcha_v2":
        import re
        match = re.search(r'data-sitekey="([^"]+)"', page.text)
        token = solve_recaptcha(match.group(1), url)
        field_name = "g-recaptcha-response"

    elif captcha_type == "turnstile":
        import re
        match = re.search(r'data-sitekey="(0x[^"]+)"', page.text)
        token = solve_turnstile(match.group(1), url)
        field_name = "cf-turnstile-response"

    elif captcha_type == "image":
        img_data = extract_captcha_image(page.text)
        token = solve_image_captcha(img_data)
        field_name = "captcha"

    return session.post(url, data={
        "drug": drug_name,
        "zip": zipcode,
        field_name: token
    })

Tekrarlı aramalarda oturum yönetimi

Fiyat karşılaştırması doğası gereği çok sayıda arama demektir. Çerezleri koruyan tek bir oturum kullanmak, her sorguda yeniden doğrulama istenmesini önler ve toplam CAPTCHA sayısını düşürür.

class PharmacySession:
    def __init__(self, base_url):
        self.session = requests.Session()
        self.base_url = base_url
        self.searches_since_captcha = 0

    def search(self, drug_name, zipcode):
        result = self.session.post(f"{self.base_url}/search", data={
            "drug": drug_name,
            "zip": zipcode
        })

        if self.is_captcha_page(result.text):
            token = self.solve_page_captcha(result.text)
            result = self.session.post(f"{self.base_url}/search", data={
                "drug": drug_name,
                "zip": zipcode,
                "g-recaptcha-response": token
            })
            self.searches_since_captcha = 0
        else:
            self.searches_since_captcha += 1

        return result

    def is_captcha_page(self, html):
        return "g-recaptcha" in html or "cf-turnstile" in html

    def solve_page_captcha(self, html):
        import re
        match = re.search(r'data-sitekey="([^"]+)"', html)
        return solve_recaptcha(match.group(1), self.base_url)

JavaScript ile paralel fiyat toplama

Node.js tarafında da yaklaşım aynıdır: her kaynak için doğrulamayı çözün, Promise.all ile paralel isteyip sonuçları fiyata göre sıralayın.

Not: Promise.all tüm kaynakları aynı anda sorgular; tek bir kaynak hata verirse try/catch onu düşürür ve kalan kaynaklar etkilenmeden döner.

async function comparePharmacyPrices(drugName, zipCode, sources) {
  const results = await Promise.all(
    sources.map(async (source) => {
      try {
        const price = await fetchDrugPrice(source, drugName, zipCode);
        return { source: source.name, price, status: 'success' };
      } catch (error) {
        return { source: source.name, error: error.message, status: 'failed' };
      }
    })
  );

  return results
    .filter(r => r.status === 'success')
    .sort((a, b) => a.price - b.price);
}

async function fetchDrugPrice(source, drugName, zipCode) {
  // Solve CAPTCHA for this source
  const token = await solveCaptcha(source.siteKey, source.url);

  const response = await fetch(source.url, {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      drug: drugName,
      zip: zipCode,
      'g-recaptcha-response': token
    })
  });

  return parsePrice(await response.text());
}

Farklı kaynaklardan gelen fiyatları normalize edin

Her portal fiyatı farklı biçimde döndürür: kimi kutu başına, kimi tablet başına, kimi de indirim uygulanmış son tutarı verir. Ham değerleri doğrudan karşılaştırırsanız yanlış bir "en düşük fiyat" seçebilirsiniz. Toplama katmanında şu alanları tek bir şemaya oturtun:

  • Birim fiyat: Tablet veya mililitre başına maliyeti hesaplayıp kaynaklar arasında kıyaslanabilir hale getirin.
  • Para birimi ve vergi: Tüm tutarları aynı para biriminde ve KDV dahil/hariç aynı temelde tutun.
  • Stok durumu: Stokta olmayan kaynağı listeden düşürün; aksi halde sıralama yanıltıcı olur.
  • Güncelleme zamanı: Her fiyatın çekildiği anı Europe/Istanbul damgasıyla saklayın ki eski veriyi taze veriyle karşılaştırmayasınız.

Bu normalize adımı olmadan karşılaştırma tablosu teknik olarak çalışır ama iş açısından güvenilmez kalır.

İstek sınırlama ve KVKK notu

Eczane portalları erişim modellerini yakından izler; çok agresif sorgu hızı sizi engellenmeye götürür. Aşağıdaki önlemler yükü doğal kullanıma yaklaştırır:

Strateji Uygulama
İstekleri aralayın Aramalar arasında 5–10 saniye bekleyin
Oturumları yenileyin Her 20–30 sorguda yeni oturum açın
Sorgu desenini çeşitlendirin Aynı ilacı art arda aramayın
Kendi altyapınızı kullanın Veri merkezi IP aralıkları daha çok CAPTCHA tetikler

Türkiye pazarında bu aracı geliştiriyorsanız, topladığınız verinin KVKK (Kişisel Verilerin Korunması Kanunu) kapsamına girip girmediğini baştan değerlendirin: kamuya açık ilaç fiyatı kişisel veri değildir, ancak sigorta formüllerinden gelen reçete bilgisi olabilir. CaptchaAI'yi yalnızca yetkili olduğunuz QA ve veri toplama akışlarında kullanın.

Uyarı: Sigorta formüllerinden reçete düzeyinde veri çekiyorsanız bu kişisel veri sayılabilir. Böyle bir alanı toplamadan önce KVKK kapsamında işleme dayanağınızı ve saklama süresini netleştirin.

Sorun giderme

Sorun Sebep Çözüm
Her aramada CAPTCHA çıkıyor Oturum çerezleri korunmuyor requests.Session() kullanın ve çerezleri saklayın
CAPTCHA sonrası fiyatlar yüklenmiyor Eksik CSRF token'ı Gizli form alanlarını çıkarıp isteğe ekleyin
Çok aramadan sonra site engelliyor İstek sınırlama İstekler arasına gecikme koyun
Tarayıcıdan farklı fiyat geliyor Eksik oturum çerezi veya başlık Tarayıcı başlıklarını birebir eşleştirin

Sık sorulan sorular

Eczane fiyatı toplamak için hangi CaptchaAI planı yeterli?

CaptchaAI thread bazlı ücretlendirir, çözüm başına değil. BASIC ($15/ay, 5 thread) planıyla 5 doğrulamayı aynı anda çözüp birkaç kaynağı paralel sorgulayabilirsiniz; daha yoğun toplama için STANDARD ($30/ay, 15 thread) kapasiteyi artırır. Aylık maliyet USD ve sabit olduğundan bütçe öngörülebilir kalır.

CaptchaAI hCaptcha'yı da çözer mi?

Hayır. CaptchaAI şu an hCaptcha ve FunCaptcha (Arkose Labs) çözmez. Eczane portallarında ağırlıklı olarak reCAPTCHA v2, Cloudflare Turnstile ve Image CAPTCHA görürsünüz; bunların üçü de desteklenir.

reCAPTCHA v2 token'ını arama formuna nasıl enjekte ederim?

solve_recaptcha fonksiyonundan dönen değeri, form gönderiminizde g-recaptcha-response alanına yerleştirin. Token dakikalar içinde geçerliliğini yitirdiği için çözüm ile POST'u art arda çalıştırın; araya uzun bekleme koymayın.

Bir portal CAPTCHA türünü değiştirirse ne yapmam gerekir?

Çıkarım kodunuzun sitekey'i hâlâ bulup bulmadığını izleyin. CaptchaAI reCAPTCHA, Turnstile ve Image gibi ana türleri tek API üzerinden çözdüğü için çoğu geçişte yalnızca captcha_type dalını güncellemeniz yeterli olur; çekirdek iş akışınız aynı kalır.

İlgili makaleler

Sonraki adımlar

Eczane fiyat karşılaştırma aracınızı kurun — CaptchaAI API anahtarınızı alın ve tüm eczane kaynaklarındaki reCAPTCHA v2 ile Turnstile doğrulamalarını tek iş akışında çözün.

Bu makale için yorumlar devre dışı bırakılmıştır.