Use Cases

CAPTCHA İşleme ile Yasal Araştırma Web Kazıma

Yasal araştırma otomasyonunda asıl darboğaz veri değil, portalların girişe koyduğu CAPTCHA duvarıdır: bir arama sayfasını çekmeye çalıştığınız anda reCAPTCHA v2 ya da eski bir image CAPTCHA karşınıza çıkar ve pipeline durur. CaptchaAI bu iki türü API üzerinden çözer, böylece içtihat taraması, başvuru izleme ve düzenleyici veri toplama işiniz kesintisiz akar. PACER, SEC EDGAR, patent veritabanları ve eyalet mahkemesi sistemleri gibi kaynaklarda çözümü g-recaptcha-response token'ı ya da OCR yanıtı olarak alır, formu gönderir ve sonuç sayfasını ayrıştırırsınız.

Bu rehber; kaynak-CAPTCHA eşleşmesini, tekrar kullanılabilir bir Python kazıyıcısını, düzenleyici izleme döngüsünü, en sık takıldığınız noktaları ve KVKK açısından dikkat edilmesi gerekenleri kapsar.


Hangi yasal veri kaynakları CAPTCHA kullanıyor?

Bir iş akışı tasarlamadan önce hangi kaynağın hangi CAPTCHA türünü tetiklediğini bilmek gerekir. Federal sistemler ağırlıklı olarak reCAPTCHA v2 kullanırken, eyalet mahkemeleri hâlâ eski image CAPTCHA'larına yaslanır — ikisi farklı çözüm yolları ister.

Kaynak CAPTCHA Türü Veri Kullanıcılar
PACER reCAPTCHA v2 Federal mahkeme kayıtları Dava ekipleri
Eyalet mahkeme sistemleri Resim CAPTCHA / reCAPTCHA Eyalet vaka kayıtları Avukatlar
SEC EDGAR reCAPTCHA v2 Kurumsal başvurular Uyumluluk
Patent veritabanları reCAPTCHA v2 Patent kayıtları Fikri mülkiyet araştırmacıları
Düzenleyici portallar Resim CAPTCHA'sı Kurallar, rehberlik Uyumluluk
Yasal alıntı veritabanları reCAPTCHA v2 Vaka alıntıları Hukuk teknolojisi
Barolar Birliği rehberleri reCAPTCHA v2 Avukat kayıtları Durum tespiti

İki tür de CaptchaAI'nin GA kapsamındadır; reCAPTCHA v2 için userrecaptcha, image/OCR için base64 yöntemi kullanılır. hCaptcha bu portallarda nadir görülür ve CaptchaAI tarafından desteklenmediği için iş akışınızı reCAPTCHA v2 ve image CAPTCHA üzerine kurmanız daha güvenlidir.


İçtihat arama motorunu kurma

Aşağıdaki LegalResearchScraper sınıfı tek bir oturum içinde her iki CAPTCHA türünü de ele alır: sayfada data-sitekey görürse reCAPTCHA'yı çözer, aksi halde image CAPTCHA'yı indirip OCR'a gönderir. Arama, detay çekme ve dosya (docket) izleme aynı oturumu paylaşır.

import requests
import time
import re
import base64
from bs4 import BeautifulSoup
import csv

CAPTCHAAI_KEY = "YOUR_API_KEY"
CAPTCHAAI_URL = "https://ocr.captchaai.com"


def solve_recaptcha(sitekey, pageurl):
    resp = requests.post(f"{CAPTCHAAI_URL}/in.php", data={
        "key": CAPTCHAAI_KEY, "method": "userrecaptcha",
        "googlekey": sitekey, "pageurl": pageurl, "json": 1,
    })
    task_id = resp.json()["request"]
    for _ in range(60):
        time.sleep(5)
        result = requests.get(f"{CAPTCHAAI_URL}/res.php", params={
            "key": CAPTCHAAI_KEY, "action": "get",
            "id": task_id, "json": 1,
        })
        data = result.json()
        if data["request"] != "CAPCHA_NOT_READY":
            return data["request"]
    raise TimeoutError("Timeout")


def solve_image_captcha(image_bytes):
    img_b64 = base64.b64encode(image_bytes).decode()
    resp = requests.post(f"{CAPTCHAAI_URL}/in.php", data={
        "key": CAPTCHAAI_KEY, "method": "base64",
        "body": img_b64, "json": 1,
    })
    task_id = resp.json()["request"]
    for _ in range(20):
        time.sleep(3)
        result = requests.get(f"{CAPTCHAAI_URL}/res.php", params={
            "key": CAPTCHAAI_KEY, "action": "get",
            "id": task_id, "json": 1,
        })
        data = result.json()
        if data["request"] != "CAPCHA_NOT_READY":
            return data["request"]
    raise TimeoutError("Timeout")


class LegalResearchScraper:
    def __init__(self, proxy=None):
        self.session = requests.Session()
        if proxy:
            self.session.proxies = {"http": proxy, "https": proxy}
        self.session.headers.update({
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
            "AppleWebKit/537.36 Chrome/126.0.0.0 Safari/537.36",
        })

    def search_cases(self, search_url, query, sitekey=None, max_pages=5):
        """Search case law database."""
        all_cases = []

        for page in range(max_pages):
            url = f"{search_url}?q={query}&page={page + 1}"
            resp = self.session.get(url, timeout=30)

            if self._has_captcha(resp.text):
                if sitekey:
                    token = solve_recaptcha(sitekey, url)
                    resp = self.session.post(url, data={
                        "q": query,
                        "g-recaptcha-response": token,
                    })
                else:
                    resp = self._solve_image_and_retry(resp.text, url, query)

            cases = self._parse_cases(resp.text)
            if not cases:
                break

            all_cases.extend(cases)
            print(f"Page {page + 1}: {len(cases)} cases")
            time.sleep(5)

        return all_cases

    def get_case_details(self, case_url):
        """Fetch full case details."""
        resp = self.session.get(case_url, timeout=30)

        if self._has_captcha(resp.text):
            sitekey = self._extract_sitekey(resp.text)
            if sitekey:
                token = solve_recaptcha(sitekey, case_url)
                resp = self.session.post(case_url, data={
                    "g-recaptcha-response": token,
                })

        soup = BeautifulSoup(resp.text, "html.parser")
        return {
            "title": self._text(soup, "h1, .case-title"),
            "citation": self._text(soup, ".citation, .case-cite"),
            "court": self._text(soup, ".court, .jurisdiction"),
            "date": self._text(soup, ".decision-date, .date-decided"),
            "judge": self._text(soup, ".judge, .authored-by"),
            "summary": self._text(soup, ".summary, .headnote"),
            "url": case_url,
        }

    def monitor_docket(self, docket_url, case_number, sitekey=None):
        """Monitor a specific case docket for new filings."""
        resp = self.session.get(docket_url, timeout=30)

        data = {"case_number": case_number}
        if sitekey and self._has_captcha(resp.text):
            token = solve_recaptcha(sitekey, docket_url)
            data["g-recaptcha-response"] = token

        resp = self.session.post(docket_url, data=data)
        return self._parse_docket(resp.text)

    def export_results(self, cases, filename):
        """Export case results to CSV."""
        if not cases:
            return
        with open(filename, "w", newline="", encoding="utf-8") as f:
            writer = csv.DictWriter(f, fieldnames=cases[0].keys())
            writer.writeheader()
            writer.writerows(cases)

    def _has_captcha(self, html):
        return any(tag in html.lower() for tag in [
            'data-sitekey', 'g-recaptcha', 'captcha',
        ])

    def _extract_sitekey(self, html):
        match = re.search(r'data-sitekey="([^"]+)"', html)
        return match.group(1) if match else None

    def _solve_image_and_retry(self, html, url, query):
        match = re.search(r'src="(/captcha[^"]+)"', html)
        if match:
            img_url = url.split("?")[0].rstrip("/") + match.group(1)
            img = self.session.get(img_url)
            answer = solve_image_captcha(img.content)
            return self.session.post(url, data={
                "q": query,
                "captcha": answer,
            })
        return self.session.get(url)

    def _parse_cases(self, html):
        soup = BeautifulSoup(html, "html.parser")
        cases = []
        for item in soup.select(".case-result, .search-result, tr.result"):
            title_el = item.select_one("a, .case-name")
            if title_el:
                cases.append({
                    "title": title_el.get_text(strip=True),
                    "url": title_el.get("href", ""),
                    "citation": self._text(item, ".citation, .cite"),
                    "date": self._text(item, ".date"),
                    "court": self._text(item, ".court"),
                })
        return cases

    def _parse_docket(self, html):
        soup = BeautifulSoup(html, "html.parser")
        entries = []
        for row in soup.select(".docket-entry, tr.filing"):
            entries.append({
                "date": self._text(row, ".date, td:first-child"),
                "entry": self._text(row, ".description, td:nth-child(2)"),
                "filed_by": self._text(row, ".filer, td:nth-child(3)"),
            })
        return entries

    def _text(self, el, selector):
        found = el.select_one(selector)
        return found.get_text(strip=True) if found else ""


# Usage
scraper = LegalResearchScraper(
    proxy="http://user:pass@residential.proxy.com:5000"
)

# Search case law
cases = scraper.search_cases(
    search_url="https://caselaw.example.com/search",
    query="data privacy GDPR",
    max_pages=5,
)

# Get details for relevant cases
for case in cases[:10]:
    if case["url"]:
        details = scraper.get_case_details(case["url"])
        print(f"{details['citation']}: {details['title']}")
        time.sleep(3)

# Export results
scraper.export_results(cases, "gdpr_cases.csv")

Kod, çözüm bittikten sonra token'ı doğrudan form gönderimine (g-recaptcha-response) yerleştirir ve sayfalar arasında time.sleep(5) ile bekler; bu bekleme, portalın istek sınırına (rate limit) takılmamak için önemlidir.


Düzenleyici başvuruları otomatik izleme

Aynı kazıyıcının üstüne kurulan RegulatoryMonitor, birden çok düzenleyici portalı periyodik olarak tarar ve yalnızca daha önce görülmemiş başvuruları döndürür. Alıntı (citation) ya da başlık bazında tekilleştirme yaparak aynı kaydı iki kez raporlamayı önler.

class RegulatoryMonitor:
    def __init__(self, proxy=None):
        self.scraper = LegalResearchScraper(proxy=proxy)
        self.seen_entries = set()

    def check_new_filings(self, feeds):
        """Check regulatory portals for new filings."""
        new_filings = []

        for feed in feeds:
            try:
                cases = self.scraper.search_cases(
                    feed["url"], feed["query"],
                    sitekey=feed.get("sitekey"),
                    max_pages=2,
                )

                for case in cases:
                    key = case.get("citation") or case.get("title")
                    if key and key not in self.seen_entries:
                        self.seen_entries.add(key)
                        case["source"] = feed["name"]
                        new_filings.append(case)

            except Exception as e:
                print(f"Error checking {feed['name']}: {e}")

            time.sleep(5)

        return new_filings

seen_entries kümesini kalıcı bir depoya (ör. Redis ya da bir CSV) taşırsanız, izleme sürecini yeniden başlattığınızda geçmiş başvuruları tekrar bildirim olarak almazsınız.


Sık karşılaşılan sorunlar ve çözümleri

Yasal portallar hem CAPTCHA hem de agresif istek sınırlaması uygular. Aşağıdaki tablo, üretimde en çok karşılaşılan durumları ve pratik çözümlerini özetler.

Sorun Sebep Çözüm
Resim CAPTCHA tekrar tekrar başarısız oluyor Aşırı bozulmuş metin Yanıtı raporlayıp yeniden gönderin; yeni bir resim üretilir
PACER erişimi engelliyor İstek sınırı aşıldı 30 dakika bekleyin, istek sıklığını düşürün
Vaka ayrıntıları eksik geliyor İçerik ödeme duvarının arkasında Gerektiğinde sayfa başına ücreti ödeyin
Arama sonuç döndürmüyor Ayrıştırmadan önce CAPTCHA sayfası geldi Parse etmeden önce _has_captcha ile kontrol edin
Dosya (docket) izleme başvuru kaçırıyor Kontrol aralığı çok uzun max_pages ve kontrol sıklığını artırın

KVKK ve yasal veri toplama

Türkiye'den çalışan hukuk teknolojisi ve uyumluluk ekipleri için teknik akış kadar hukuki çerçeve de önemlidir. Mahkeme kararlarındaki taraf adları, avukat kayıtları ve baro rehberleri kişisel veri içerebilir; bu veriler kazındığında KVKK (Kişisel Verilerin Korunması Kanunu) kapsamına girer. Bu nedenle CAPTCHA çözümünü yalnızca yetkili araştırma, durum tespiti veya kamuya açık kayıt derleme gibi meşru amaçlarla ve portalın kullanım şartlarına uygun biçimde kullanın.

Pratik bir kural: kamuya açık içtihat ve başvuru verisini toplarken kişisel veriyi yalnızca amacınızla sınırlı ölçüde saklayın, Europe/Istanbul saat diliminde çalışan izleme işlerinizi düşük sıklıkta planlayın ve gereksiz yükü portala bindirmeyin. USD bazlı, thread temelli fiyatlandırma (ör. BASIC $15/ay, 5 thread) bu tür sürekli izleme işlerinde aylık maliyeti öngörülebilir kılar — TL kuru dalgalansa da bütçeniz sabit kalır.


Sık sorulan sorular

CaptchaAI bu portallarda hangi CAPTCHA türlerini çözüyor?

Yasal veritabanlarında en çok görülen reCAPTCHA v2 (userrecaptcha) ile image/OCR CAPTCHA'larını (base64) çözer; ayrıca reCAPTCHA v3, Cloudflare Turnstile ve GeeTest v3 desteklenir. hCaptcha ve FunCaptcha desteklenmez, bu yüzden iş akışınızı bunlara bağlamayın.

reCAPTCHA v2 çözümünün aylık maliyeti ne olur?

CaptchaAI çözüm başına değil, eşzamanlı thread başına ücretlendirir ve her planda çözüm sayısı sınırsızdır. Küçük bir izleme işi için BASIC ($15/ay, 5 thread) genelde yeterlidir; paralel portal sayısı arttıkça STANDARD ($30/ay, 15 thread) veya ADVANCE ($90/ay, 50 thread) planına geçebilirsiniz.

PACER ücretlerini nasıl yönetirim?

PACER sayfa başına ücret alır; bu, CAPTCHA çözümünden bağımsız bir maliyettir. Toplu veri toplamayı planlarken bu ücretlere bütçe ayırın ve daha önce indirilmiş belgeler için ücretsiz RECAP arşivini değerlendirin.

Image CAPTCHA sürekli başarısız olursa ne yapmalıyım?

Bozuk yanıtı raporlayıp yeniden gönderdiğinizde sisteme yeni bir resim gelir; genellikle ikinci denemede çözülür. Kalıcı başarısızlıkta sayfanın gerçekten bir image CAPTCHA mı yoksa reCAPTCHA mı sunduğunu (data-sitekey var mı) doğrulayın.

Mahkeme kayıtlarını otomatik kazımak yasal mı?

Kamuya açık mahkeme kayıtları genel olarak erişilebilir olsa da, erişim yöntemi ne olursa olsun sistemin istek sınırlarına ve kullanım şartlarına uymanız gerekir. Kişisel veri içeren kayıtlar için KVKK yükümlülüklerini gözetin ve toplamayı yetkili amaçlarla sınırlayın.


İlgili kılavuzlar


Yasal araştırmayı hızlandırın — CaptchaAI anahtarınızı alın ve içtihat ile başvuru aramalarını otomatikleştirin.

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