Use Cases

Penanganan CAPTCHA untuk Otomatisasi Pencarian Domain WHOIS

Jawaban singkatnya: saat portal WHOIS menampilkan reCAPTCHA v2, ambil sitekey dari halaman, kirim ke CaptchaAI, lalu suntikkan token g-recaptcha-response yang dikembalikan ke request lookup Anda. Dengan pola ini, satu pemeriksaan yang tadinya macet di halaman verifikasi bisa berjalan penuh tanpa intervensi manual. Sisanya hanya soal mengatur jeda antar-kueri agar CAPTCHA tidak muncul terlalu sering.

Masalahnya nyata bagi siapa pun yang bekerja dengan data domain di Indonesia — reseller domain, agensi price-monitoring, sampai freelancer scraping di Upwork atau Fastwork. Portal WHOIS menjaga data pendaftaran domain dengan reCAPTCHA v2, CAPTCHA gambar, dan pembatasan laju permintaan. Entah Anda mengecek ketersediaan domain, memverifikasi kepemilikan, atau memantau tanggal kedaluwarsa, verifikasi biasanya muncul setelah beberapa kueri saja. Artikel ini menunjukkan alur teknis untuk menanganinya.

Kapan CAPTCHA Muncul di Portal WHOIS

Tidak semua portal WHOIS berperilaku sama. Ambang pemicu dan jenis tantangan bergantung pada siapa yang mengelola portalnya. Tabel berikut merangkum pola yang paling sering ditemui:

Jenis portal CAPTCHA Ambang pemicu
ICANN WHOIS reCAPTCHA v2 3–5 query per sesi
Halaman pencarian registrar reCAPTCHA v2/v3 5–10 query per menit
NIR Regional (APNIC, RIPE) CAPTCHA gambar 10–20 query
WHOIS lelang domain Cloudflare Turnstile Query cepat berturut-turut
Alat WHOIS massal CAPTCHA kustom Setelah batas tier gratis

Yang perlu diingat: reCAPTCHA v2 mendominasi portal registrar dan ICANN, sedangkan CAPTCHA gambar lebih umum di registry regional. CaptchaAI menangani ketiganya — reCAPTCHA v2, CAPTCHA gambar (OCR), dan Cloudflare Turnstile — lewat endpoint yang sama, jadi kode Anda tidak perlu bercabang per portal.

Alur Lookup WHOIS dengan Penyelesaian reCAPTCHA

Pola intinya sederhana dan konsisten dengan seluruh API CaptchaAI: deteksi CAPTCHA → ekstrak sitekey → kirim ke in.php → polling res.php → suntikkan token. Kelas WhoisLookup di bawah membungkus alur itu, plus helper untuk bulk lookup dan cek ketersediaan. Perhatikan bahwa _solve_recaptcha melakukan polling setiap 3 detik hingga 60 kali sebelum menyerah — cukup untuk waktu penyelesaian reCAPTCHA v2 yang wajar.

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']}")

Untuk cek ketersediaan domain massal, check_availability menjalankan bulk_lookup lalu memilah domain menjadi tersedia atau terpakai. Jeda delay default 3 detik antar-kueri sengaja ada untuk menekan frekuensi CAPTCHA — bukan sekadar sopan santun jaringan, tapi taktik agar Anda tidak menghabiskan thread untuk menyelesaikan CAPTCHA yang sebetulnya bisa dihindari.

Pemantauan Kedaluwarsa Domain Otomatis

Kasus pemakaian kedua adalah memantau kapan domain akan kedaluwarsa. Kelas DomainMonitor dalam JavaScript menyimpan daftar pantau, menjalankan lookup terjadwal, dan menandai domain yang tersisa 30 hari atau kurang. Alur penyelesaian reCAPTCHA-nya identik dengan versi Python — hanya beda bahasa.

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`));

Jalankan checkExpirations() sekali sehari lewat cron atau scheduler, dan Anda punya sistem peringatan dini kedaluwarsa domain yang otomatis menyelesaikan CAPTCHA apa pun yang menghadang di tengah jalan.

Menekan Frekuensi CAPTCHA

Menyelesaikan CAPTCHA memang mudah, tapi lebih baik menghindarinya. Semakin sedikit CAPTCHA yang muncul, semakin sedikit thread yang terpakai dan semakin cepat batch Anda selesai. Empat strategi berikut paling berdampak:

Strategi Manfaat
Cache hasil secara lokal Hindari lookup berulang untuk domain yang sama
Gunakan delay 3–5 detik Kurangi frekuensi pemicu CAPTCHA
Rotasi antar portal WHOIS Distribusikan beban ke beberapa provider
Pertahankan sesi (session) Simpan status izin CAPTCHA yang sudah lolos

Untuk deployment yang menyasar registry regional Asia, menempatkan worker di region cloud terdekat — misalnya AWS ap-southeast-1 (Singapura) atau ap-southeast-3 (Jakarta) — memangkas latensi round-trip dan membuat batas timeout polling lebih longgar.

Mengatasi Masalah Umum

Masalah Penyebab Perbaikan
CAPTCHA setelah 3 query Rate limit portal Naikkan delay, gunakan proxy
WHOIS mengembalikan "No match" Redaksi privasi/RDAP Coba portal WHOIS alternatif
Token reCAPTCHA ditolak Token kedaluwarsa sebelum dikirim Selesaikan dan kirim dalam 2 menit
IP diblokir Melebihi batas query harian Rotasi egress jaringan yang diotorisasi

Biaya untuk Volume Tinggi

CaptchaAI menagih per thread konkuren, bukan per solve, dengan solve tak terbatas per thread selama satu bulan tagihan. Untuk pemantauan domain yang berjalan sekali sehari, paket BASIC ($15/bulan, 5 thread) sudah lebih dari cukup. Bila Anda menjalankan bulk lookup ribuan domain secara paralel, ADVANCE ($90/bulan, 50 thread) memberi ruang konkurensi yang jauh lebih besar tanpa biaya tambahan per CAPTCHA. Model biaya tetap ini terasa berbeda dibanding penetapan harga per-solve — beban Anda dapat diprediksi berapa pun volume kuerinya. Harga terkini selalu ada di halaman pricing CaptchaAI.

Satu catatan kepatuhan yang relevan di Indonesia: data WHOIS kerap memuat informasi pribadi pendaftar. UU Pelindungan Data Pribadi (UU 27/2022) menuntut Anda hanya memproses data yang memang berhak Anda olah — batasi pengumpulan pada field yang Anda butuhkan dan hindari menyimpan data pribadi yang tidak diperlukan.

Pertanyaan Umum

Apakah CaptchaAI mendukung CAPTCHA gambar di portal WHOIS regional seperti APNIC?

Ya. Selain reCAPTCHA v2, CaptchaAI menyelesaikan CAPTCHA gambar (OCR) yang umum dipakai registry regional. Anda mengirim gambarnya ke endpoint yang sama dan menerima teks hasilnya, jadi satu integrasi menangani beragam jenis portal.

Berapa biaya menangani ribuan lookup WHOIS per bulan?

Biaya bergantung pada konkurensi, bukan jumlah solve. Karena setiap paket memberi solve tak terbatas per thread, ribuan lookup per bulan tetap masuk dalam satu paket tetap — mulai BASIC ($15/bulan, 5 thread) untuk beban ringan hingga ADVANCE ($90/bulan, 50 thread) untuk pemrosesan paralel besar.

Apakah scraping data WHOIS berisiko dari sisi perlindungan data?

Bisa, karena record WHOIS sering memuat data pribadi pendaftar. Di Indonesia, UU Pelindungan Data Pribadi mewajibkan Anda hanya mengolah data yang berhak diproses. Batasi pada field yang relevan bagi kebutuhan Anda dan jangan menyimpan data pribadi yang tidak diperlukan. Artikel ini bukan nasihat hukum.

Kapan sebaiknya memakai protokol WHOIS port 43 daripada portal web?

Port 43 tidak memunculkan CAPTCHA, tapi rate limit-nya ketat dan datanya sering terpotong akibat redaksi privasi. Portal web umumnya menampilkan lebih banyak field — dengan konsekuensi CAPTCHA yang harus diselesaikan. Banyak alur menggabungkan keduanya: port 43 untuk cek cepat, portal web saat butuh detail lengkap.

Artikel Terkait

Langkah Selanjutnya

Otomatiskan pencarian domain Anda — ambil API key CaptchaAI dan tangani CAPTCHA portal WHOIS sejak lookup pertama.

Komentar dinonaktifkan untuk artikel ini.