Kasus Penggunaan

Pemantauan Rantai Pasokan dengan Penanganan CAPTCHA

Visibilitas rantai pasokan memerlukan data dari ratusan portal pemasok, platform logistik, dan sistem inventaris. Banyak dari mereka melindungi data mereka di balik CAPTCHA. CaptchaAI menangani tantangan ini sehingga pemantauan saluran pipa berjalan tanpa gangguan.


Dimana CAPTCHA Memblokir Data Rantai Pasokan

Jenis Sumber Jenis CAPTCHA Data Frecookiensi
Portal pemasok reCAPTCHA v2 Persediaan, harga, waktu tunggu Setiap hari
Operator pengiriman Cloudflare Turnstile Pelacakan, tarif, ETA pengiriman Setiap jam
Katalog pabrikan Gambar CAPTCHA Spesifikasi produk, MOQ Mingguan
Portal bea cukai reCAPTCHA v2 Tarif bea, kode tarif Setiap hari
Otoritas pelabuhan Gambar CAPTCHA Jadwal kapal, kemacetan pelabuhan Setiap 6 jam
Pertukaran komoditas reCAPTCHA v3 Harga spot, kontrak berjangka Waktu nyata

Monitor Multi-Pemasok

import requests
import time
import re
import json
import base64
from datetime import datetime

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_turnstile(sitekey, pageurl):
    resp = requests.post(f"{CAPTCHAAI_URL}/in.php", data={
        "key": CAPTCHAAI_KEY, "method": "turnstile",
        "sitekey": 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(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 SupplyChainMonitor:
    def __init__(self, suppliers, proxy=None):
        self.suppliers = suppliers
        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 check_all(self):
        """Check inventory and pricing across all suppliers."""
        report = {
            "timestamp": datetime.now().isoformat(),
            "suppliers": {},
        }

        for supplier in self.suppliers:
            try:
                data = self._check_supplier(supplier)
                report["suppliers"][supplier["name"]] = {
                    "status": "success",
                    "data": data,
                }
            except Exception as e:
                report["suppliers"][supplier["name"]] = {
                    "status": "error",
                    "error": str(e),
                }
            time.sleep(3)

        return report

    def _check_supplier(self, supplier):
        url = supplier["url"]
        resp = self.session.get(url, timeout=30)

        # Handle CAPTCHA based on type
        captcha_type = supplier.get("captcha_type")
        if captcha_type and self._has_captcha(resp.text):
            resp = self._solve_captcha(resp, url, supplier)

        from bs4 import BeautifulSoup
        soup = BeautifulSoup(resp.text, "html.parser")

        return {
            "products": self._extract_inventory(soup),
            "last_updated": self._extract_date(soup),
        }

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

    def _solve_captcha(self, resp, url, supplier):
        captcha_type = supplier.get("captcha_type", "recaptcha")
        sitekey = supplier.get("sitekey", "")

        if not sitekey:
            match = re.search(r'data-sitekey="([^"]+)"', resp.text)
            sitekey = match.group(1) if match else ""

        if captcha_type == "turnstile":
            token = solve_turnstile(sitekey, url)
            return self.session.post(url, data={"cf-turnstile-response": token})
        elif captcha_type == "image":
            match = re.search(r'src="(/captcha[^"]+)"', resp.text)
            if match:
                img_resp = self.session.get(url.rstrip("/") + match.group(1))
                answer = solve_image(img_resp.content)
                return self.session.post(url, data={"captcha": answer})
        else:
            token = solve_recaptcha(sitekey, url)
            return self.session.post(url, data={"g-recaptcha-response": token})

        return resp

    def _extract_inventory(self, soup):
        items = []
        for row in soup.select("table.inventory tr, .product-row"):
            cols = row.select("td, .col")
            if len(cols) >= 3:
                items.append({
                    "sku": cols[0].get_text(strip=True),
                    "stock": cols[1].get_text(strip=True),
                    "price": cols[2].get_text(strip=True),
                })
        return items

    def _extract_date(self, soup):
        date_el = soup.select_one(".last-updated, .update-time")
        return date_el.get_text(strip=True) if date_el else ""


# Configure suppliers
suppliers = [
    {
        "name": "Supplier A",
        "url": "https://supplier-a.example.com/inventory",
        "captcha_type": "recaptcha",
        "sitekey": "6Lc_xxxxxxx",
    },
    {
        "name": "Carrier B",
        "url": "https://carrier-b.example.com/rates",
        "captcha_type": "turnstile",
        "sitekey": "0x4AAAAAAA_xxx",
    },
    {
        "name": "Manufacturer C",
        "url": "https://manufacturer-c.example.com/catalog",
        "captcha_type": "image",
    },
]

monitor = SupplyChainMonitor(
    suppliers=suppliers,
    proxy="http://user:pass@residential.proxy.com:5000",
)
report = monitor.check_all()
print(json.dumps(report, indent=2))

Pemantauan Tarif Pengiriman

class ShippingRateTracker:
    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 get_rates(self, carrier_url, origin, destination, weight):
        """Fetch shipping rates, handling Turnstile CAPTCHA."""
        resp = self.session.get(carrier_url, timeout=30)

        sitekey_match = re.search(r'data-sitekey="([^"]+)"', resp.text)
        if sitekey_match:
            token = solve_turnstile(sitekey_match.group(1), carrier_url)
            resp = self.session.post(carrier_url, data={
                "origin": origin,
                "destination": destination,
                "weight": weight,
                "cf-turnstile-response": token,
            })

        if resp.status_code == 200:
            return resp.json().get("rates", [])
        return []

Peringatan Perubahan Stok

def monitor_with_alerts(monitor, alert_thresholds, check_interval=3600):
    """Continuously monitor and alert on inventory changes."""
    previous_data = {}

    while True:
        report = monitor.check_all()

        for supplier, info in report["suppliers"].items():
            if info["status"] != "success":
                continue

            for product in info["data"].get("products", []):
                sku = product["sku"]
                stock = product.get("stock", "")

                # Parse stock level
                try:
                    stock_qty = int(re.sub(r'\D', '', stock))
                except ValueError:
                    continue

                key = f"{supplier}:{sku}"
                prev_qty = previous_data.get(key, stock_qty)

                threshold = alert_thresholds.get(sku, 10)
                if stock_qty < threshold and prev_qty >= threshold:
                    print(f"ALERT: {supplier} - {sku} dropped to {stock_qty}")

                previous_data[key] = stock_qty

        time.sleep(check_interval)

Kebijakan penggantian penyedia

  • Pisahkan pemasok penting dari sumber opsional sehingga satu kegagalan tidak menghapus seluruh laporan pemantauan.
  • Tentukan kapan data usang dapat diterima dan kapan pemasok yang hilang harus memicu insiden eksplisit.
  • Tingkatkan tekanan CAPTCHA yang terus-menerus pada pemasok karena risiko pengadaan, bukan hanya kesalahan teknis.

Kebijakan penggantian penyedia

  • Pisahkan pemasok penting dari sumber opsional sehingga satu kegagalan tidak menghapus seluruh laporan pemantauan.
  • Tentukan kapan data usang dapat diterima dan kapan pemasok yang hilang harus memicu insiden eksplisit.
  • Tingkatkan tekanan CAPTCHA yang terus-menerus pada pemasok karena risiko pengadaan, bukan hanya kesalahan teknis.

Pemecahan Masalah

Masalah Penyebab Solusi
Tata letak halaman pemasok diubah Desain ulang situs Perbarui pemilih CSS
CAPTCHA di setiap cek Terlalu sering memeriksa Tingkatkan interval antar pemeriksaan
Sesi berakhir di tengah pemeriksaan Timeout portal habis Gunakan sticky session, periksa lebih cepat
Nilai data hilang Diperlukan login Tambahkan langkah autentikasi
Harga yang ditampilkan salah Penetapan harga berbasis geografis Cocokkan lokasi proxy dengan pasar

Pertanyaan Umum

Seberapa sering saya harus memeriksa inventaris pemasok?

Setiap hari untuk sebagian besar pemasok. Setiap jam untuk komponen penting selama kekurangan pasokan. Pemeriksaan yang terlalu sering memicu CAPTCHA lebih cepat.

Bisakah saya memantau ratusan pemasok?

Ya. Rotasi antar pemasok dengan penundaan di antara masing-masing pemasok. Gunakan proxy yang berputar untuk mendistribusikan beban ke seluruh IP.

Jenis CAPTCHA manakah yang paling umum di situs rantai pasokan?

reCAPTCHA v2 di portal pemasok, Cloudflare Turnstile di situs logistik/carrier. Situs pabrikan lama sering kali menggunakan CAPTCHA gambar.


Panduan Terkait

  • Memutar egress jaringan yang diotorisasi
  • Sticky Session vs Berputar
  • Kegigihan Sesi Browser

Jaga agar rantai pasokan Anda tetap terlihat —dapatkan kunci CaptchaAI Andadan mengotomatiskan pengumpulan data di seluruh portal pemasok.

Komentar dinonaktifkan untuk artikel ini.