Tim legaltech dan kepatuhan yang memantau putusan pengadilan atau filing regulasi kerap berhenti di satu titik: halaman pencarian meminta reCAPTCHA v2 atau image CAPTCHA sebelum menampilkan hasil. Artikel ini menunjukkan cara mengotomatiskan riset hukum di Python — meneruskan tantangan CAPTCHA ke CaptchaAI, lalu mengembalikan token ke form pencarian agar scraping berlanjut tanpa intervensi manual.
Fokusnya pada reCAPTCHA v2 dan image CAPTCHA, dua tipe yang paling sering muncul di portal hukum. Bagian berikut menjelaskan alur token CaptchaAI, kelas scraper yang bisa dipakai ulang, pemantauan filing regulasi secara berkala, dan catatan kepatuhan untuk pasar Indonesia.
Portal riset hukum yang memasang CAPTCHA
Jenis CAPTCHA menentukan metode API: reCAPTCHA v2 lewat userrecaptcha, image CAPTCHA lewat base64.
| Sumber | Jenis CAPTCHA | Data | Pengguna |
|---|---|---|---|
| PACER | reCAPTCHA v2 | Filing pengadilan federal | Tim litigasi |
| Sistem pengadilan negara bagian | Image CAPTCHA / reCAPTCHA | Catatan kasus negara bagian | Pengacara |
| SEC EDGAR | reCAPTCHA v2 | Filing perusahaan | Kepatuhan |
| Database paten | reCAPTCHA v2 | Catatan paten | Peneliti IP |
| Portal regulasi | Image CAPTCHA | Aturan, panduan | Kepatuhan |
| Database sitasi hukum | reCAPTCHA v2 | Sitasi kasus | Legaltech |
| Direktori asosiasi advokat | reCAPTCHA v2 | Catatan pengacara | Due diligence |
Alur token CaptchaAI dalam empat langkah
Baik reCAPTCHA v2 maupun image CAPTCHA mengikuti pola yang sama. Pahami empat langkah ini sekali, dan sisanya hanya soal menempelkan token ke form yang tepat:
- Kirim task ke
in.php— sertakansitekeydan URL halaman untuk reCAPTCHA v2, atau gambar ber-base64untuk image CAPTCHA. - Simpan task ID dari respons
in.php. - Polling
res.phphingga token siap, dengan jeda beberapa detik antar-cek agar tidak membebani API. - Pakai token pada request pencarian berikutnya sebagai
g-recaptcha-responseatau field jawaban image.
Kode di bawah membungkus keempat langkah ini agar scraper Anda tidak perlu mengulang logika polling di setiap fungsi.
Membangun scraper pencarian kasus hukum
Kelas berikut membungkus tiga pekerjaan inti — cari kasus, ambil detail, pantau docket. Setiap request mengecek apakah halaman mengembalikan CAPTCHA; jika ya, token CaptchaAI dilampirkan sebagai g-recaptcha-response atau field jawaban image sebelum request diulang.
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")
Perhatikan LegalResearchScraper memakai satu requests.Session, sehingga cookie dan header tetap konsisten antar-request — penting saat portal seperti PACER mengamati perilaku sesi sebelum memutuskan menampilkan CAPTCHA.
Memantau filing regulasi baru secara otomatis
Untuk kepatuhan, kebutuhan tersering adalah pemantauan berkala. Kelas RegulatoryMonitor menyimpan sitasi dan judul yang sudah diproses di sebuah set, lalu hanya mengembalikan filing baru.
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
Jalankan pemantau lewat penjadwal seperti cron; deploy di region seperti AWS ap-southeast-3 (Jakarta) menjaga latensi rendah.
Kepatuhan dan konteks pasar Indonesia
Di Indonesia, riset hukum otomatis paling sering dijalankan oleh:
- Startup legaltech yang membangun mesin pencari putusan dan sitasi kasus.
- Tim kepatuhan perusahaan yang memantau perubahan regulasi sektoral.
- Konsultan hukum yang menyusun due diligence untuk klien.
Apa pun kasusnya, satu prinsip universal berlaku: kumpulkan hanya data yang boleh Anda proses. UU Pelindungan Data Pribadi (UU 27/2022) menegaskan data pribadi tidak boleh dikumpulkan tanpa dasar yang sah — batasi scraping pada dokumen publik dan hindari data pribadi di balik login. Ini bukan nasihat hukum; saat ragu, konsultasikan tim legal Anda.
Mengatasi masalah umum saat scraping
Sebagian besar kegagalan scraping riset hukum berulang di titik yang sama. Berikut penyebab tersering beserta cara menanganinya:
| Masalah | Penyebab | Solusi |
|---|---|---|
| Image CAPTCHA gagal berulang kali | Teks terdistorsi | Laporkan dan coba lagi dengan gambar baru |
| PACER memblokir akses | Rate limit terlampaui | Tunggu 30 menit, kurangi frekuensi request |
| Detail kasus tidak lengkap | Di balik paywall | Bayar biaya per halaman bila perlu |
| Pencarian tidak menghasilkan apa pun | Halaman CAPTCHA dikembalikan | Periksa CAPTCHA sebelum parsing |
| Filing baru terlewat oleh monitor | Interval pengecekan terlalu panjang | Perpendek interval, tingkatkan frekuensi cek |
Pertanyaan umum
Apakah CaptchaAI mendukung reCAPTCHA v2 dan image CAPTCHA untuk portal hukum?
Ya, keduanya didukung — dan itu dua tipe tersering di database hukum. reCAPTCHA v2 biasanya selesai dalam waktu di bawah 60 detik, sedangkan image CAPTCHA jauh lebih cepat. hCaptcha dan FunCaptcha belum didukung; jika sebuah situs memakainya, Anda perlu solusi lain.
Berapa thread yang dibutuhkan untuk scraping database hukum?
Bergantung pada tingkat paralelisme. Untuk pemantauan satu atau dua portal, BASIC ($15/bulan, 5 thread) biasanya cukup; naikkan ke STANDARD ($30/bulan, 15 thread) saat menambah sumber. Semua paket memberi solve tanpa batas per thread, jadi biaya tetap prediktabel.
Bagaimana mengatur frekuensi request agar tidak memicu rate limit?
Beri jeda antar-request (skrip ini memakai time.sleep(5)), batasi max_pages, dan pantau respons 429. Untuk sumber ketat seperti PACER, kurangi paralelisme dan perpanjang jeda; perhatikan tarif per halaman PACER.
Bagaimana menjaga scraping riset hukum tetap patuh terhadap UU PDP?
Kumpulkan hanya dokumen publik dan hindari data pribadi di balik login. UU Pelindungan Data Pribadi (UU 27/2022) mensyaratkan dasar yang sah untuk memproses data pribadi, jadi batasi cakupan pada putusan, sitasi, dan filing yang memang terbuka untuk umum. Untuk kasus abu-abu, konsultasikan tim legal Anda; artikel ini bukan nasihat hukum.
Bisakah pemantauan filing regulasi dijalankan otomatis di server?
Bisa. Bungkus RegulatoryMonitor dalam skrip terjadwal seperti cron, lalu deploy di region terdekat seperti AWS ap-southeast-3 (Jakarta) agar latensi ke portal target tetap rendah. Simpan sitasi yang sudah diproses di dalam set agar hanya filing baru yang dilaporkan pada setiap siklus.
Panduan terkait
- Otomatisasi portal pemerintah dengan penyelesaian CAPTCHA
- Scraping riset akademik dengan penanganan CAPTCHA
- Rotasi egress jaringan yang diotorisasi