Khả năng hiển thị chuỗi cung ứng yêu cầu dữ liệu từ hàng trăm cổng thông tin nhà cung cấp, nền tảng hậu cần và hệ thống kiểm kê. Nhiều người trong số này bảo vệ dữ liệu của họ bằng CAPTCHA. CaptchaAI xử lý những thách thức này để các đường ống giám sát hoạt động không bị gián đoạn.
Trường hợp CAPTCHA chặn dữ liệu chuỗi cung ứng
| Loại nguồn | Loại CAPTCHA | dữ liệu | Tần số |
|---|---|---|---|
| Cổng thông tin nhà cung cấp | reCAPTCHA v2 | Hàng tồn kho, giá cả, thời gian giao hàng | hàng ngày |
| hãng vận chuyển | Cloudflare Turnstile | Theo dõi, giá cả, ETA giao hàng | Hàng giờ |
| Danh mục nhà sản xuất | CAPTCHA hình ảnh | Thông số sản phẩm, MOQ | hàng tuần |
| Cổng thông tin hải quan | reCAPTCHA v2 | Thuế suất, mã số thuế | hàng ngày |
| Chính quyền cảng | CAPTCHA hình ảnh | Lịch tàu, ùn tắc cảng | Cứ sau 6 giờ |
| Trao đổi hàng hóa | reCAPTCHA v3 | Giá giao ngay, giá kỳ hạn | thời gian thực |
Màn hình đa nhà cung cấp
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))
Giám sát giá vận chuyển
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 []
Cảnh báo về thay đổi hàng tồn kho
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)
Chính sách dự phòng của nhà cung cấp
- Tách các nhà cung cấp quan trọng khỏi các nguồn tùy chọn để một lỗi không làm mất đi toàn bộ báo cáo giám sát.
- Xác định khi nào dữ liệu cũ được chấp nhận và khi nào nhà cung cấp bị thiếu phải gây ra sự cố rõ ràng.
- Tăng áp lực CAPTCHA dai dẳng lên nhà cung cấp như một rủi ro tìm nguồn cung ứng chứ không chỉ là lỗi kỹ thuật.
Khắc phục sự cố
| Vấn đề | Nguyên nhân | Cách xử lý |
|---|---|---|
| Bố cục trang nhà cung cấp đã thay đổi | Thiết kế lại trang web | Cập nhật bộ chọn CSS |
| CAPTCHA trên mỗi lần kiểm tra | Kiểm tra quá thường xuyên | Tăng khoảng thời gian giữa các lần kiểm tra |
| Phiên hết hạn khi đang kiểm tra | Hết thời gian chờ của cổng thông tin | Sử dụng phiên dính, kiểm tra nhanh hơn |
| Thiếu dữ liệu về giá | Yêu cầu đăng nhập | Thêm bước xác thực |
| Giá hiển thị sai | Định giá dựa trên địa lý | Khớp vị trí proxy với thị trường |
Câu hỏi thường gặp
Tôi nên kiểm tra hàng tồn kho của nhà cung cấp bao lâu một lần?
Hàng ngày đối với hầu hết các nhà cung cấp. Hàng giờ cho các thành phần quan trọng trong thời gian thiếu nguồn cung. Việc kiểm tra quá thường xuyên sẽ kích hoạt CAPTCHA nhanh hơn.
Tôi có thể giám sát hàng trăm nhà cung cấp không?
Vâng. Luân chuyển giữa các nhà cung cấp với độ trễ giữa mỗi nhà cung cấp. Sử dụng proxy luân phiên để phân phối tải trên các IP.
Loại CAPTCHA nào phổ biến nhất trên các trang web của chuỗi cung ứng?
reCAPTCHA v2 trên cổng nhà cung cấp, Cloudflare Turnstile trên các trang web logistics/carrier. Các trang web cũ của nhà sản xuất thường sử dụng CAPTCHA hình ảnh.
Hướng dẫn liên quan
Giữ cho chuỗi cung ứng của bạn luôn hiển thị —lấy khóa CaptchaAI của bạnvà tự động hóa việc thu thập dữ liệu trên tất cả các cổng của nhà cung cấp.