Kullanım Senaryoları

CBS ve Haritalama Veri Çıkarımı için CAPTCHA Kullanımı

Devletin GIS portalları, ilçe değerlendirme sistemleri ve haritalama platformları, Görüntü ve OCR CAPTCHA'ları ile coğrafi sorguları korur. Bu portallar parsel sınırlarına, imar belirlemelerine, taşkın bölgelerine ve mülk değerlendirmelerine (gayrimenkul analizi, şehir planlaması ve çevre araştırmaları için değerli veriler) hizmet verir. CAPTCHA'ları nasıl ele alacağınız aşağıda açıklanmıştır.

GIS Portallarındaki CAPTCHA Desenleri

Portal türü CAPTCHA türü Tetikleyici
İlçe GIS/assessor Resim metni CAPTCHA Parsel arama sorguları
Devlet coğrafi portalları Özel CAPTCHA Veri indirme istekleri
USGS veri portalları reCAPTCHA v2 Toplu veri erişimi
Belediye imar haritaları Resim CAPTCHA'sı Tekrarlanan mülk aramaları
Çevresel veritabanları Matematik CAPTCHA'sı Rapor oluşturma
Taşkın bölgesi araması Resim metni CAPTCHA Adres sorguları

CBS Veri Çıkarıcı

import requests
import base64
import time
import re

class GISDataExtractor:
    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_parcel(self, portal_url, parcel_id):
        """Look up parcel data by ID, solving CAPTCHAs as needed."""
        response = self.session.get(
            f"{portal_url}/parcel", params={"id": parcel_id}
        )

        if self._has_image_captcha(response.text):
            captcha_url = self._extract_captcha_url(response.text, portal_url)
            captcha_text = self._solve_captcha(captcha_url)

            # Re-submit with solved CAPTCHA
            response = self.session.post(f"{portal_url}/parcel", data={
                "id": parcel_id,
                "captcha": captcha_text,
                **self._extract_hidden_fields(response.text)
            })

        return self._parse_parcel_data(response.text)

    def search_by_address(self, portal_url, address):
        """Search GIS records by street address."""
        response = self.session.get(
            f"{portal_url}/search", params={"address": address}
        )

        if self._has_image_captcha(response.text):
            captcha_url = self._extract_captcha_url(response.text, portal_url)
            captcha_text = self._solve_captcha(captcha_url)

            response = self.session.post(f"{portal_url}/search", data={
                "address": address,
                "captcha": captcha_text,
                **self._extract_hidden_fields(response.text)
            })

        return self._parse_search_results(response.text)

    def bulk_extract(self, portal_url, parcel_ids, delay=3):
        """Extract data for multiple parcels with rate limiting."""
        results = {}

        for parcel_id in parcel_ids:
            try:
                results[parcel_id] = self.lookup_parcel(portal_url, parcel_id)
            except Exception as e:
                results[parcel_id] = {"error": str(e)}
            time.sleep(delay)

        return results

    def _has_image_captcha(self, html):
        return bool(re.search(
            r'captcha|verification.?image|security.?code',
            html, re.IGNORECASE
        ))

    def _extract_captcha_url(self, html, base_url):
        from bs4 import BeautifulSoup
        from urllib.parse import urljoin
        soup = BeautifulSoup(html, "html.parser")

        img = (
            soup.find("img", attrs={"src": lambda s: s and "captcha" in s.lower()}) or
            soup.find("img", {"id": re.compile(r"captcha", re.I)}) or
            soup.find("img", {"class": re.compile(r"captcha", re.I)})
        )

        if img and img.get("src"):
            return urljoin(base_url, img["src"])
        raise ValueError("CAPTCHA image not found")

    def _solve_captcha(self, captcha_url):
        """Download and solve image CAPTCHA."""
        img_response = self.session.get(captcha_url)
        img_base64 = base64.b64encode(img_response.content).decode("utf-8")

        resp = requests.post("https://ocr.captchaai.com/in.php", data={
            "key": self.api_key,
            "method": "base64",
            "body": img_base64,
            "json": 1
        })
        task_id = resp.json()["request"]

        for _ in range(30):
            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("CAPTCHA solve timed out")

    def _extract_hidden_fields(self, html):
        from bs4 import BeautifulSoup
        soup = BeautifulSoup(html, "html.parser")
        fields = {}
        for inp in soup.select("input[type='hidden']"):
            name = inp.get("name")
            if name:
                fields[name] = inp.get("value", "")
        return fields

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

        def text_or_none(node):
            return node.text.strip() if node and node.text else None

        return {
            "parcel_id": text_or_none(soup.select_one(".parcel-id, #parcelId")),
            "owner": text_or_none(soup.select_one(".owner, .owner-name")),
            "address": text_or_none(soup.select_one(".address, .situs")),
            "zoning": text_or_none(soup.select_one(".zoning, .zone-code")),
            "acreage": text_or_none(soup.select_one(".acreage, .area")),
            "assessed_value": text_or_none(soup.select_one(".assessed, .value")),
            "land_use": text_or_none(soup.select_one(".land-use, .use-code"))
        }

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

        def text_or_none(node):
            return node.text.strip() if node and node.text else None

        results = []
        for row in soup.select(".result-row, tr.parcel"):
            results.append({
                "parcel_id": text_or_none(row.select_one(".parcel-id")),
                "address": text_or_none(row.select_one(".address")),
                "owner": text_or_none(row.select_one(".owner"))
            })
        return results


# Usage
extractor = GISDataExtractor("YOUR_API_KEY")

# Single parcel lookup
parcel = extractor.lookup_parcel(
    "https://gis.county.example.gov",
    "12-34-567-890"
)
print(f"Owner: {parcel['owner']}, Zoning: {parcel['zoning']}")

# Bulk extraction
parcels = extractor.bulk_extract(
    "https://gis.county.example.gov",
    ["12-34-567-890", "12-34-567-891", "12-34-567-892"]
)

Koordinat Tabanlı Çıkarma (JavaScript)

class GISExtractor {
  constructor(apiKey) {
    this.apiKey = apiKey;
  }

  async extractByCoordinates(portalUrl, lat, lng) {
    const url = `${portalUrl}/identify?lat=${lat}&lng=${lng}`;
    const response = await fetch(url);
    const html = await response.text();

    if (this.hasCaptcha(html)) {
      return this.solveAndExtract(portalUrl, html, { lat, lng });
    }

    return this.parseGISData(html);
  }

  async extractRegion(portalUrl, bounds, gridSize = 0.01) {
    const results = [];
    const { north, south, east, west } = bounds;

    for (let lat = south; lat <= north; lat += gridSize) {
      for (let lng = west; lng <= east; lng += gridSize) {
        try {
          const data = await this.extractByCoordinates(portalUrl, lat, lng);
          if (data.parcelId) results.push(data);
        } catch (error) {
          console.error(`Failed at ${lat},${lng}: ${error.message}`);
        }
        // Rate limit
        await new Promise(r => setTimeout(r, 2000));
      }
    }

    return results;
  }

  hasCaptcha(html) {
    return /captcha|verification.?image|security.?code/i.test(html);
  }

  async solveAndExtract(portalUrl, html, params) {
    const imgMatch = html.match(/src="([^"]*captcha[^"]*)"/i);
    if (!imgMatch) throw new Error('CAPTCHA image not found');

    const imgUrl = new URL(imgMatch[1], portalUrl).href;
    const imgResp = await fetch(imgUrl);
    const buffer = await imgResp.arrayBuffer();
    const base64 = Buffer.from(buffer).toString('base64');

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

    for (let i = 0; i < 30; 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(portalUrl, {
          method: 'POST',
          body: new URLSearchParams({
            ...params,
            captcha: data.request
          })
        });
        return this.parseGISData(await response.text());
      }
    }
    throw new Error('CAPTCHA solve timed out');
  }

  parseGISData(html) {
    return {
      parcelId: html.match(/parcel.?id[^>]*>([^<]+)/i)?.[1]?.trim(),
      zoning: html.match(/zon(?:e|ing)[^>]*>([^<]+)/i)?.[1]?.trim(),
      acreage: html.match(/acreage|area[^>]*>([^<]+)/i)?.[1]?.trim(),
      landUse: html.match(/land.?use[^>]*>([^<]+)/i)?.[1]?.trim()
    };
  }
}

// Usage
const gis = new GISExtractor('YOUR_API_KEY');

// Single coordinate lookup
const data = await gis.extractByCoordinates(
  'https://gis.county.example.gov',
  34.0522, -118.2437
);

// Extract entire region
const region = await gis.extractRegion('https://gis.county.example.gov', {
  north: 34.10, south: 34.00, east: -118.20, west: -118.30
});

CBS Portalları için CAPTCHA Parametreleri

Parametre Değer Kullanım örneği
method base64 Standart resim CAPTCHA
numeric 1 Yalnızca sayısal CAPTCHA'lar
min_len 4 Karakter sayısı bilindiğinde
max_len 6 Karakter sayısı bilindiğinde
language 0 Turkish/Latin karakterler
textinstructions Özel Matematik CAPTCHA'ları veya biçimlendirilmiş kodlar

Seri öncesi ekstraksiyon kontrol listesi

  • Büyük bir koleksiyon çalıştırmasına başlamadan önce harita görünüm penceresini, bölge filtresini ve sayfalandırma kontrollerini doğrulayın.
  • Normalleştirilmiş koordinat yükünü ve ham hedef yanıtını saklayın, böylece ayıklama hataları hata ayıklamaya devam edebilir.
  • CAPTCHA yoğunluğu arttığında, yeniden denemelerin hedef taraftaki davranış değişikliğini gizlemesine izin vermek yerine toplu işlemi duraklatın.

Sorun giderme

Sorun Sebep Düzeltme
CAPTCHA resim yüklemeleri bozuk Oturum çerezi gerekli Önce arama sayfasını yükle
Çözülmüş metin reddedildi Büyük/küçük harf duyarlılığı case_sensitive=1 parametresini ekle
Portal farklı CAPTCHA döndürüyor Oturuma özel CAPTCHA Aynı oturumda indirin ve çözün
CAPTCHA'dan sonra paket verisi yok Gizli form alanları eksik Göndermeden önce tüm gizli girişleri çıkarın

SSS

CBS portalları neden eski tarz görüntü CAPTCHA'larını kullanıyor?

Devlet CBS sistemleri genellikle modern CAPTCHA hizmetlerinden önce gelen eski platformlar üzerine kuruludur. Bütçe kısıtlamaları ve uzun satın alma döngüleri, bu eski CAPTCHA'ların devam ettiği anlamına geliyor.

İlçeye özgü CAPTCHA formatlarını nasıl işlemeliyim?

Her ilçe farklı CAPTCHA uygulamalarını kullanabilir. Belirli bir biçimi tanımlamak için CaptchaAI'nin textinstructions parametresini kullanın; örneğin, "5 büyük harf" veya "matematik denklemini çözün."

CAPTCHA'ların arkasından şekil dosyası veya GeoJSON verilerini çıkarabilir miyim?

Portal, bir CAPTCHA'nın arkasında indirilebilir konumsal veriler sunuyorsa, indirme bağlantısına erişmek için CAPTCHA'yı çözün. CaptchaAI CAPTCHA'yı yönetir; daha sonra dosyayı normal şekilde indirin.

Sonraki Adımlar

GIS verilerini güvenilir bir şekilde çıkarın —CaptchaAI API anahtarınızı alınve devlet portalı CAPTCHA'larını otomatik olarak yönetin.


Sonraki adımlar

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