Use Cases

WHOIS Arama Otomasyonunda CAPTCHA Çözme

Toplu alan adı uygunluk kontrolü yapan bir betik, çoğu WHOIS portalında ilk birkaç sorgudan sonra bir reCAPTCHA v2 duvarına toslar ve döngü orada takılır. Çözüm portalı değiştirmek değil: sayfadaki sitekey'i çıkarıp CaptchaAI API'sine göndermek, geri gelen token'ı forma yerleştirmek ve sorguya kaldığı yerden devam etmek. Aşağıda reCAPTCHA v2, resim CAPTCHA'sı ve Cloudflare Turnstile ile korunan portallar için çalışan bir akış var.

WHOIS otomasyonunu ne zaman kullanırsınız

WHOIS sorgularını elle yapmak birkaç alan adı için sorun değildir; iş bir listeye dönüşünce durum değişir. Türkiye'deki serbest otomasyon geliştiricileri ve e-ticaret ekipleri bu akışı çoğunlukla dört senaryoda kurar:

  • Uygunluk kontrolü: bir marka için yüzlerce alan adı varyasyonunun boşta olup olmadığını taramak.
  • Sahiplik doğrulama: bir alan adının kayıt şirketini ve kayıt tarihini teyit etmek.
  • Son kullanma takibi: portföydeki alan adlarının yenileme tarihini kaçırmamak.
  • Marka izleme: benzer alan adlarının kimler tarafından alındığını periyodik olarak kontrol etmek.

Dördünde de darboğaz aynıdır: portal, birkaç sorgudan sonra CAPTCHA gösterir ve otomasyon durur. Bu yüzden önce CAPTCHA'nın nerede tetiklendiğini bilmek gerekir.

Portal türüne göre CAPTCHA eşikleri

CAPTCHA'lar rastgele belirmez; portal türüne ve sorgu hızınıza bağlı bir eşiği vardır. Bu eşiği bilmek, betiğinizin nerede duracağını önceden görmenizi sağlar.

Portal türü CAPTCHA Tetik eşiği
ICANN WHOIS reCAPTCHA v2 Oturum başına 3-5 sorgu
Kayıt şirketi arama sayfaları reCAPTCHA v2/v3 Dakikada 5-10 sorgu
Bölgesel NIR (APNIC, RIPE) Resim CAPTCHA'sı 10-20 sorgu
Alan adı açık artırma WHOIS'i Cloudflare Turnstile Hızlı alan adı kontrollerinde
Toplu WHOIS araçları Özel CAPTCHA Ücretsiz seviye sınırından sonra

Çoğu portalda birincil engel reCAPTCHA v2 olduğundan, ana örneği onun üzerine kuruyoruz; aynı desen resim CAPTCHA'sı ve Turnstile için de geçerlidir.

reCAPTCHA v2'yi çözüp WHOIS verisini çekme (Python)

Akış dört adımdır: yanıt HTML'inde reCAPTCHA olup olmadığını kontrol edin, data-sitekey değerini çıkarın, sitekey ile sayfa adresini CaptchaAI'ye gönderip token'ı bekleyin ve token'ı g-recaptcha-response alanıyla forma yollayın. Görevi in.php ile gönderir, sonucu res.php üzerinden status 1 dönene kadar sorgularsınız.

import requests
import time
import re

class WhoisLookup:
    def __init__(self, api_key):
        self.api_key = api_key
        self.session = requests.Session()
        self.session.headers.update({
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
        })

    def lookup(self, domain, whois_url):
        """Look up WHOIS data for a domain, solving CAPTCHAs as needed."""
        response = self.session.get(whois_url, params={"domain": domain})

        if self._has_recaptcha(response.text):
            site_key = self._extract_site_key(response.text)
            token = self._solve_recaptcha(site_key, whois_url)
            response = self.session.post(whois_url, data={
                "domain": domain,
                "g-recaptcha-response": token
            })

        return self._parse_whois(response.text)

    def bulk_lookup(self, domains, whois_url, delay=3):
        """Look up WHOIS for multiple domains."""
        results = {}
        for domain in domains:
            try:
                results[domain] = self.lookup(domain, whois_url)
            except Exception as e:
                results[domain] = {"error": str(e)}
            time.sleep(delay)
        return results

    def check_availability(self, domains, whois_url):
        """Check which domains are available for registration."""
        results = self.bulk_lookup(domains, whois_url)
        available = []
        taken = []

        for domain, data in results.items():
            if data.get("error") or data.get("status") == "available":
                available.append(domain)
            else:
                taken.append(domain)

        return {"available": available, "taken": taken}

    def _has_recaptcha(self, html):
        return "g-recaptcha" in html or "recaptcha" in html.lower()

    def _extract_site_key(self, html):
        match = re.search(r'data-sitekey="([^"]+)"', html)
        if match:
            return match.group(1)
        raise ValueError("reCAPTCHA site key not found")

    def _solve_recaptcha(self, site_key, page_url):
        resp = requests.post("https://ocr.captchaai.com/in.php", data={
            "key": self.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": self.api_key,
                "action": "get",
                "id": task_id,
                "json": 1
            })
            data = result.json()
            if data["status"] == 1:
                return data["request"]

        raise TimeoutError("reCAPTCHA solve timed out")

    def _parse_whois(self, html):
        from bs4 import BeautifulSoup
        soup = BeautifulSoup(html, "html.parser")

        # Look for WHOIS data in pre-formatted blocks or tables
        raw_whois = soup.select_one("pre, .whois-data, #whois-result")
        if raw_whois:
            text = raw_whois.get_text()
            return self._extract_fields(text)

        return {"raw": soup.get_text()[:2000]}

    def _extract_fields(self, text):
        fields = {}
        patterns = {
            "registrar": r"Registrar:\s*(.+)",
            "created": r"Creat(?:ed|ion) Date:\s*(.+)",
            "expires": r"(?:Expir(?:y|ation)|Registry Expiry) Date:\s*(.+)",
            "updated": r"Updated Date:\s*(.+)",
            "status": r"(?:Domain )?Status:\s*(.+)",
            "nameservers": r"Name Server:\s*(.+)",
            "registrant": r"Registrant (?:Name|Organization):\s*(.+)"
        }

        for field, pattern in patterns.items():
            matches = re.findall(pattern, text, re.IGNORECASE)
            if matches:
                fields[field] = matches if len(matches) > 1 else matches[0].strip()

        return fields


# Usage
whois = WhoisLookup("YOUR_API_KEY")

# Single lookup
result = whois.lookup("example.com", "https://whois.example.com/lookup")
print(f"Registrar: {result.get('registrar')}")
print(f"Expires: {result.get('expires')}")

# Bulk availability check
domains = ["startup-name.com", "my-project.io", "cool-app.dev"]
availability = whois.check_availability(domains, "https://whois.example.com/lookup")
print(f"Available: {availability['available']}")

Token elinize geçtiği andan itibaren kısa ömürlüdür; bu yüzden çözümle gönderimi aynı akış içinde tutun ve araya elle bekleme koymayın.

Toplu sorgu ve uygunluk kontrolü

bulk_lookup, listedeki her alan adı için aynı çözüm mantığını uygular ve sorgular arasına delay saniyelik bir bekleme koyar; bu, portalı yormadan ilerlemenin en basit yoludur. check_availability ise dönen sonuçları "boşta" ve "alınmış" olarak ikiye ayırır. Bir müşteri için yüzlerce alan adı varyasyonunu tararken tek ihtiyacınız, listeyi bu iki fonksiyona geçirmektir.

Alan adı son kullanma tarihlerini izleme (Node.js)

Aynı mantık izleme için de geçerli. Aşağıdaki DomainMonitor bir izleme listesi tutar, her alan adı için WHOIS verisini çeker ve son kullanmaya 30 günden az kaldıysa uyarı üretir. reCAPTCHA v2 çıktığında solveAndLookup devreye girip sitekey'i CaptchaAI'ye gönderir.

class DomainMonitor {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.watchList = new Map();
  }

  addDomain(domain, whoisUrl) {
    this.watchList.set(domain, { url: whoisUrl, history: [] });
  }

  async checkExpirations() {
    const expiring = [];

    for (const [domain, config] of this.watchList) {
      try {
        const data = await this.lookup(domain, config.url);
        config.history.push({ ...data, checkedAt: new Date().toISOString() });

        if (data.expires) {
          const daysLeft = Math.ceil(
            (new Date(data.expires) - new Date()) / (1000 * 60 * 60 * 24)
          );
          if (daysLeft <= 30) {
            expiring.push({ domain, daysLeft, expires: data.expires });
          }
        }
      } catch (error) {
        console.error(`Failed to check ${domain}: ${error.message}`);
      }
    }

    return expiring;
  }

  async lookup(domain, whoisUrl) {
    const response = await fetch(`${whoisUrl}?domain=${domain}`);
    const html = await response.text();

    if (html.includes('g-recaptcha')) {
      return this.solveAndLookup(domain, whoisUrl, html);
    }

    return this.parseWhois(html);
  }

  async solveAndLookup(domain, whoisUrl, html) {
    const match = html.match(/data-sitekey="([^"]+)"/);
    if (!match) throw new Error('No reCAPTCHA site key found');

    const submitResp = await fetch('https://ocr.captchaai.com/in.php', {
      method: 'POST',
      body: new URLSearchParams({
        key: this.apiKey,
        method: 'userrecaptcha',
        googlekey: match[1],
        pageurl: whoisUrl,
        json: '1'
      })
    });
    const { request: taskId } = await submitResp.json();

    for (let i = 0; i < 60; i++) {
      await new Promise(r => setTimeout(r, 3000));
      const result = await fetch(
        `https://ocr.captchaai.com/res.php?key=${this.apiKey}&action=get&id=${taskId}&json=1`
      );
      const data = await result.json();
      if (data.status === 1) {
        const response = await fetch(whoisUrl, {
          method: 'POST',
          body: new URLSearchParams({
            domain,
            'g-recaptcha-response': data.request
          })
        });
        return this.parseWhois(await response.text());
      }
    }
    throw new Error('reCAPTCHA solve timed out');
  }

  parseWhois(html) {
    const extract = (pattern) => {
      const match = html.match(pattern);
      return match ? match[1].trim() : null;
    };

    return {
      registrar: extract(/Registrar:\s*([^\n<]+)/i),
      created: extract(/Creat(?:ed|ion) Date:\s*([^\n<]+)/i),
      expires: extract(/(?:Expir(?:y|ation)|Registry Expiry) Date:\s*([^\n<]+)/i),
      status: extract(/(?:Domain )?Status:\s*([^\n<]+)/i)
    };
  }
}

// Usage
const monitor = new DomainMonitor('YOUR_API_KEY');
monitor.addDomain('example.com', 'https://whois.example.com/lookup');
monitor.addDomain('mysite.io', 'https://whois.example.com/lookup');

const expiring = await monitor.checkExpirations();
expiring.forEach(d => console.log(`${d.domain} expires in ${d.daysLeft} days`));

Bu izleyiciyi Europe/Istanbul saatine göre günlük veya haftalık zamanlanmış bir görevle çalıştırın; portföyünüzdeki alan adları için yenileme tarihini kaçırmazsınız.

Sorguları CAPTCHA tetiklemeden yürütme

Her sorguyu CAPTCHA'ya çevirmek gereksiz. Web tabanlı çoğu WHOIS portalı, hız sınırlaması başlamadan önce IP başına günde 50-200 sorguya izin verir. Aşağıdaki alışkanlıklar bu sınıra takılma sıklığını düşürür ve çözüm hacmini azaltır.

Strateji Fayda
Sonuçları yerelde önbelleğe alın Aynı alan adı için tekrar sorgu yapmayın
3-5 saniyelik gecikme koyun CAPTCHA tetiklenme oranını düşürün
Portalları dönüşümlü kullanın Yükü sağlayıcılar arasına dağıtın
Oturumu kalıcı tutun Kazanılmış CAPTCHA durumunu koruyun

WHOIS verisi ve KVKK

WHOIS kayıtları alan adı sahibinin adı, e-postası ve adresi gibi kişisel verileri içerebilir. Türkiye'de bu veriler KVKK (Kişisel Verilerin Korunması Kanunu) kapsamına girer; çoğu kayıt şirketi de zaten GDPR/RDAP redaksiyonu uygular. Otomatik WHOIS sorgularını yetkili bir QA veya veri toplama iş akışıyla sınırlı tutun ve topladığınız kişisel veri için meşru bir hukuki dayanağınız olduğundan emin olun. Redakte edilmiş kayıtları yeniden kişiselleştirmeye çalışmak bu çerçevenin dışına çıkar.

Sık karşılaşılan sorunlar

Sorun Sebep Düzeltme
3 sorgudan sonra CAPTCHA Portal hız sınırı Gecikmeyi artırın, sorgu hacmini düşürün
WHOIS "Eşleşme yok" veriyor Gizlilik/RDAP redaksiyonu Alternatif WHOIS portalını deneyin
reCAPTCHA token'ı reddedildi Token gönderilmeden önce süresi doldu 2 dakika içinde çözüp gönderin
Engellenen IP Günlük sorgu sınırı aşıldı Farklı bir proxy üzerinden çıkın, hacmi azaltın

Sık sorulan sorular

reCAPTCHA token'ı neden "reddedildi" hatası veriyor?

Neredeyse her zaman zaman aşımıdır: reCAPTCHA v2 token'ı üretildikten sonra yaklaşık iki dakika geçerlidir. Çözümle gönderim arasında uzun gecikme varsa portal token'ı reddeder. Çözümü ve g-recaptcha-response gönderimini tek akışta tutun.

WHOIS protokolü (port 43) yerine web portallarını mı kullanmalıyım?

Port 43 WHOIS'te CAPTCHA yoktur ama katı hız sınırları ve GDPR redaksiyonu nedeniyle sınırlı veri döner. Yalnızca kayıt şirketi ve son kullanma tarihi gerekiyorsa port 43 yeterli olabilir; ayrıntılı kayıt için web portalı gerekir.

Topladığım WHOIS verisi KVKK kapsamına girer mi?

Kayıt sahibinin adı ve iletişim bilgileri kişisel veriyse evet, KVKK kapsamına girer. Bu veriyi yalnızca yetkili ve meşru bir amaç için, saklama kurallarına uyarak işleyin.

CaptchaAI'yi WHOIS otomasyonu için kullanmak ne kadar tutar?

Fiyatlandırma thread tabanlıdır; her plan aylık sınırsız çözüm içerir. BASIC ($15/ay, 5 thread) çoğu sorgu döngüsü için yeterlidir. USD üzerinden öngörülebilir aylık ücret, TL dalgalanmasına karşı da bir avantajdır.

İlgili makaleler

Sonraki adımlar

WHOIS aramalarını otomatikleştirin — CaptchaAI API anahtarınızı alın ve portal CAPTCHA'larını akışınızda çözün.

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