
Emma Foster
Machine Learning Engineer

AntiCloudflareTask resmi dengan proxy statis atau sticky.Pemantauan inventaris e-commerce yang andal adalah masalah bukti, bukan hanya masalah pengambilan halaman. Halaman produk dapat menunjukkan "tersedia" sementara ukuran tertentu tidak tersedia, API pasar dapat tertinggal dari feed merchant, dan tantangan Cloudflare dapat menggantikan halaman yang diharapkan dengan respons interstitial. Alur kerja yang benar adalah API-first, sadar varian, dan konsisten sesi. Menggunakan feed resmi di mana tersedia, merekam pengamatan ketersediaan yang terstruktur, dan memanggil CapSolver hanya ketika tantangan Cloudflare yang didukung mengganggu fallback browser yang diizinkan. Panduan ini menjelaskan model data, alur pemulihan tantangan, persyaratan proxy statis dan user-agent, handoff cookie, deteksi perubahan inventaris, kontrol notifikasi, dan batas kepatuhan untuk operasi ritel, intelijen katalog, dan pemantauan ketersediaan yang disetujui.
Pemantauan inventaris harus menjawab satu pertanyaan operasional spesifik. Contoh umum meliputi:
Hindari target yang samar seperti "pantau produk ini." Tetapkan identifikasi produk, varian, wilayah, konteks pengiriman, sumber, dan kondisi notifikasi.
inventory_job = {
"canonical_product_id": "catalog-7821",
"gtin": "0099999999999",
"variant": {
"color": "hitam",
"size": "M",
},
"market": "US",
"destination_postal_code": "94107",
"sources": [
"merchant_inventory_feed",
"marketplace_api",
"authorized_product_page",
],
"alert_on": ["OUT_OF_STOCK_TO_IN_STOCK"],
}
Blog e-commerce CapSolver https://www.capsolver.com/blog/ecommerce menutupi alur kerja komersial terkait, dan FAQ web-scraping CapSolver https://www.capsolver.com/faq/web-scraping menjelaskan pertimbangan operasional untuk pengumpulan data publik yang diizinkan.
Sumber resmi biasanya lebih stabil dan lebih mudah diaudit. Gunakan feed merchant, API penjual, titik akhir inventaris pasar, dan penyedia katalog lisensi sebelum membaca halaman yang ditampilkan pembeli.
Dokumentasi API Browse eBay https://developer.ebay.com/api-docs/buy/browse/resources/item/methods/search mendukung pencarian item berdasarkan kata kunci, kategori, ePID, GTIN, kondisi, dan filter lainnya. Untuk toko yang mempublikasikan halaman produk yang terstruktur, Schema.org Offer menentukan field seperti availability, price, priceCurrency, seller, dan kuantitas yang memenuhi syarat. Dokumentasi data terstruktur Google https://developers.google.com/search/docs/appearance/structured-data/product-snippet menjelaskan bagaimana data penawaran dan ketersediaan dapat muncul dalam markup produk.
| Sumber | Peran yang direkomendasikan | Kekuatan utama | Keterbatasan utama |
|---|---|---|---|
| Feed inventaris merchant | Primer untuk katalog yang dimiliki | Data SKU dan kuantitas langsung | Terbatas pada hubungan komersial Anda |
| API pasar | Primer untuk daftar pasar yang disetujui | Identifikasi dan filter yang terstruktur | Kuota dan bidang khusus pasar |
| Penyedia lisensi | Normalisasi lintas pasar | Skema yang konsisten | Biaya lisensi dan cakupan |
| Halaman publik yang diizinkan | Validasi dan penutup celah | Mencerminkan status yang ditampilkan pembeli | Perubahan tata letak dan validasi lalu lintas |
Pengumpulan browser harus memvalidasi atau melengkapi celah data yang diketahui, bukan menggantikan sumber resmi yang tersedia.
Kolom in_stock: true yang umum tidak cukup. Pertahankan varian, saluran, pasar, penjual, dan bukti.
from dataclasses import dataclass, field
from datetime import datetime, timezone
@dataclass
class InventoryObservation:
source: str
canonical_product_id: str
source_item_id: str | None
gtin: str | None
variant: dict[str, str]
market: str
seller_id: str | None
availability: str
quantity: int | None
quantity_confidence: str
delivery_method: str | None
store_id: str | None
source_url: str | None
evidence: dict
parser_version: str
observed_at: str = field(
default_factory=lambda: datetime.now(timezone.utc).isoformat()
)
Gunakan kamus ketersediaan yang dikendalikan:
VALID_AVAILABILITY = {
"IN_STOCK",
"OUT_OF_STOCK",
"PREORDER",
"BACKORDER",
"LIMITED",
"UNKNOWN",
}
Jika halaman hanya menyebutkan "tersedia," catat kuantitas sebagai None. Jangan menebak nilai numerik.
Garis panduan data web Python CapSolver menyediakan konteks implementasi, sementara glosarium CapSolver dapat membantu tim menyamakan istilah.
Parser harus memverifikasi identitas halaman sebelum membaca data ketersediaan. Halaman tantangan dapat mengembalikan HTTP 200 dan tetap tidak memiliki elemen produk yang diharapkan.
CHALLENGE_TITLES = {
"just a moment...",
"attention required!",
}
async def classify_page(page) -> str:
title = (await page.title()).strip().lower()
html = (await page.content()).lower()
if title in CHALLENGE_TITLES:
return "CLOUDFLARE_CHALLENGE"
if "cf-chl-" in html or "challenge-platform" in html:
return "CLOUDFLARE_CHALLENGE"
if await page.locator('[data-product-id]').count():
return "PRODUCT_PAGE"
return "UNKNOWN_PAGE"
Jangan anggap tanda ini sebagai bukti universal. Pertahankan fixture khusus target dan uji terhadap halaman yang Anda izinkan aksesnya.
Halaman produk CapSolver Cloudflare menjelaskan tugas tantangan yang didukung, dan blog Cloudflare CapSolver berisi konteks pemecahan masalah.
Dokumentasi Cloudflare Challenge resmi CapSolver mendefinisikan AntiCloudflareTask.
| Field | Wajib | Penggunaan pemantauan inventaris |
|---|---|---|
type |
Ya | Tetap sebagai AntiCloudflareTask |
websiteURL |
Ya | URL produk atau daftar yang disetujui |
proxy |
Ya | Proxy statis atau sticky yang digunakan browser |
userAgent |
Tidak | User agent Chrome yang didukung dari browser |
html |
Tidak | HTML interstitial terbaru ketika diperlukan |
Solusi dapat mencakup cookie cf_clearance, token, dan user agent. Nilai-nilai ini adalah materi sesi yang bersifat sementara. Mereka harus dikonsumsi oleh runtime pemantauan, bukan disimpan di gudang analitik.
Dokumentasi Challenges Cloudflare menjelaskan tujuan dan jenis mekanisme tantangan. Kemampuan teknis tidak memberikan izin akses, sehingga kebijakan sumber tetap menjadi aturan utama.
Jangan ekspos kredensial proxy ke analis, model, log, atau notifikasi. Selesaikan profil di dalam kode yang tepercaya.
import os
from urllib.parse import urlparse
import capsolver
capsolver.api_key = os.environ["CAPSOLVER_API_KEY"]
SOURCE_POLICY = {
"shop.example.com": {
"proxy_profile": "inventory_us_west",
"max_checks_per_hour": 4,
}
}
PROXY_VAULT = {
"inventory_us_west": os.environ["INVENTORY_PROXY_US_WEST"],
}
def approved_host(url: str) -> str:
host = urlparse(url).hostname
if host not in SOURCE_POLICY:
raise PermissionError("Sumber inventaris tidak disetujui")
return host
def solve_cloudflare_challenge(
url: str,
chrome_user_agent: str,
fresh_html: str = "",
) -> dict:
host = approved_host(url)
profile = SOURCE_POLICY[host]["proxy_profile"]
task = {
"type": "AntiCloudflareTask",
"websiteURL": url,
"proxy": PROXY_VAULT[profile],
"userAgent": chrome_user_agent,
}
if fresh_html:
task["html"] = fresh_html
solution = capsolver.solve(task)
cookies = solution.get("cookies") or {}
clearance = cookies.get("cf_clearance") or solution.get("token")
if not clearance:
raise RuntimeError("Solusi tantangan tidak mencakup clearance")
return {
"cookies": cookies,
"user_agent": solution.get("userAgent") or chrome_user_agent,
"proxy_profile": profile,
}
Gunakan proxy statis atau sticky. Jangan putar identitas jaringan antara navigasi awal, penyelesaian, dan pemulihan halaman.
Buat konteks Playwright dengan proxy dan user agent yang disetujui, tangkap keadaan tantangan, dapatkan solusi, dan terapkan cookie dalam konteks yang kompatibel.
from urllib.parse import urlparse
async def recover_inventory_page(browser, url: str):
host = approved_host(url)
profile = SOURCE_POLICY[host]["proxy_profile"]
proxy = PROXY_VAULT[profile]
bootstrap_context = await browser.new_context(
proxy={"server": proxy},
)
bootstrap_page = await bootstrap_context.new_page()
await bootstrap_page.goto(url, wait_until="domcontentloaded")
state = await classify_page(bootstrap_page)
if state != "CLOUDFLARE_CHALLENGE":
return bootstrap_context, bootstrap_page, False
user_agent = await bootstrap_page.evaluate("navigator.userAgent")
html = await bootstrap_page.content()
solution = solve_cloudflare_challenge(
url=url,
chrome_user_agent=user_agent,
fresh_html=html,
)
await bootstrap_context.close()
context = await browser.new_context(
proxy={"server": proxy},
user_agent=solution["user_agent"],
)
cookie_domain = urlparse(url).hostname
await context.add_cookies([
{
"name": name,
"value": value,
"domain": cookie_domain,
"path": "/",
"secure": True,
"httpOnly": True,
}
for name, value in solution["cookies"].items()
])
page = await context.new_page()
await page.goto(url, wait_until="domcontentloaded")
return context, page, True
Format proxy yang berbeda memerlukan bidang Playwright yang berbeda. Parsing server proxy, username, dan password di adapter vault ketika diperlukan.
Utamakan JSON-LD atau kontrak halaman yang stabil daripada teks presentasi.
import json
SCHEMA_AVAILABILITY = {
"https://schema.org/InStock": "IN_STOCK",
"https://schema.org/OutOfStock": "OUT_OF_STOCK",
"https://schema.org/PreOrder": "PREORDER",
"https://schema.org/BackOrder": "BACKORDER",
"InStock": "IN_STOCK",
"OutOfStock": "OUT_OF_STOCK",
}
async def read_jsonld_offers(page) -> list[dict]:
blocks = await page.locator(
'script[type="application/ld+json"]'
).all_text_contents()
offers = []
for raw in blocks:
try:
data = json.loads(raw)
except json.JSONDecodeError:
continue
nodes = data if isinstance(data, list) else [data]
for node in nodes:
if not isinstance(node, dict):
continue
offer = node.get("offers")
if isinstance(offer, dict):
offers.append(offer)
elif isinstance(offer, list):
offers.extend(x for x in offer if isinstance(x, dict))
return offers
Normalisasi ketersediaan tanpa menginventarisasi kuantitas:
def normalize_offer_availability(offer: dict) -> tuple[str, int | None]:
raw = str(offer.get("availability", ""))
availability = SCHEMA_AVAILABILITY.get(raw, "UNKNOWN")
inventory_level = offer.get("inventoryLevel")
quantity = None
if isinstance(inventory_level, dict):
value = inventory_level.get("value")
if isinstance(value, int) and value >= 0:
quantity = value
return availability, quantity
Simpan hash dari bukti yang relevan dan versi parser. Ini membuat notifikasi dapat direproduksi tanpa menyimpan konten halaman yang tidak perlu.
Beritahu tentang transisi, bukan snapshot berulang.
def inventory_transition(previous: str, current: str) -> str | None:
if previous == current:
return None
if previous in {"OUT_OF_STOCK", "UNKNOWN"} and current == "IN_STOCK":
return "RESTOCKED"
if previous == "IN_STOCK" and current == "OUT_OF_STOCK":
return "SOLD_OUT"
return "STATUS_CHANGED"
Butuh dua pengamatan ketika sumbernya bising:
def confirmed_transition(observations: list[InventoryObservation]) -> str | None:
if len(observations) < 3:
return None
older, previous, current = observations[-3:]
if previous.availability != current.availability:
return None
return inventory_transition(older.availability, current.availability)
Contoh kedua mengurangi notifikasi yang disebabkan oleh kesalahan parser atau keadaan halaman sementara. Sesuaikan aturan dengan ritme pembaruan sumber.
Peristiwa tantangan adalah sinyal infrastruktur. Bukan perubahan inventaris.
| Metrik | Arti | Tujuan notifikasi |
|---|---|---|
inventory_restock_total |
Transisi tidak tersedia ke tersedia yang dikonfirmasi | Operasi komersial |
inventory_unknown_total |
Parser tidak dapat menentukan ketersediaan | Antrian kualitas data |
challenge_encounter_total |
Halaman yang disetujui menampilkan tantangan | Operasi otomasi |
challenge_recovery_success |
Pemulihan selesai dan halaman produk kembali | Dashboard keandalan |
challenge_loop_total |
Halaman tetap ditantang setelah pemulihan | Tinjauan operator |
Jangan klasifikasikan halaman tantangan, kesalahan HTTP, atau selektor kosong sebagai OUT_OF_STOCK.
The Pertanyaan Umum kesalahan CapSolver memberikan panduan diagnostik, dan blog otomatisasi CapSolver membahas pola pemulihan terkait.
Kode Bonus: Gunakan kode WEBS di Dasbor CapSolver untuk mendapatkan bonus tambahan 5% pada setiap pengisian ulang.
| Kontrol | Implementasi yang direkomendasikan |
|---|---|
| Izin sumber | Catatan persetujuan per-host dan batas tujuan |
| Prioritas sumber | Feed atau API sebelum fallback browser |
| Proxy | Server-side resolve static atau sticky profile |
| User agent | Identitas Chrome yang didukung sama melalui pemulihan |
| Cookies | Penyimpanan enkripsi berumur pendek; tidak ada retensi analitik |
| Retry | Satu upaya pemulihan, lalu tinjauan operator |
| Batas kecepatan | Kuota spesifik sumber dengan backoff dan jitter |
| Pemberitahuan | Pemberitahuan read-only secara default |
| Tindakan berdampak tinggi | Konfirmasi eksplisit sebelum reservasi atau pembelian |
Gunakan Pertanyaan Umum penyelesaian CAPTCHA CapSolver untuk memahami alur tugas dan halaman produk CapSolver untuk meninjau kategori solusi yang didukung.
Hanya pantau sumber yang Anda izinkan aksesnya. Ikuti lisensi API pasar, syarat merchant, batas kecepatan, persyaratan privasi, dan kontrak data inventaris. Jangan gunakan pemulihan tantangan untuk mengakses akun pribadi, dasbor penjual terbatas, catatan pembeli, atau inventaris non-publik. Pertahankan sistem read-only kecuali layanan yang disetujui terpisah menangani reservasi atau checkout dengan persetujuan manusia eksplisit.
Pemulihan tantangan Cloudflare dapat membuat pemantauan inventaris e-commerce lebih andal, tetapi hanya ketika berada di dalam pipa data yang berbasis API, sadar varian, dan terkendali kebijakan. Monitor harus memvalidasi identitas halaman, mempertahankan konsistensi proxy dan user-agent, mengonsumsi cookie penyelesaian secara singkat, menganalisis bukti ketersediaan yang terstruktur, dan memisahkan kegagalan infrastruktur dari perubahan stok nyata.
Mulai alur kerja yang disetujui dengan CapSolver, uji terhadap sumber yang terkendali, dan tambahkan retensi bukti, batas kecepatan, dan tinjauan operator sebelum memperluas.
Tidak. Utamakan feed merchant, API pasar, API penjual, dan sumber data lisensi. Gunakan browser yang diizinkan hanya untuk celah yang diizinkan atau validasi yang terlihat pengguna.
Gunakan AntiCloudflareTask yang telah didokumentasikan dengan URL target yang tepat dan proxy statis atau sticky. Bidang opsional termasuk user agent Chrome yang didukung browser dan HTML tantangan segar.
Tidak. Tantangan, halaman kesalahan, atau selektor yang hilang adalah keadaan infrastruktur atau parser. Catat UNKNOWN dan arahkan secara terpisah dari transisi inventaris.
Hanya simpan di penyimpanan runtime enkripsi berumur pendek. Jangan tempatkan di konteks model, tabel analitik, pemberitahuan, atau log jangka panjang.
Pertahankan pemantauan dalam mode read-only. Reservasi, checkout, dan pembelian memerlukan layanan yang disetujui terpisah, validasi harga segar, batas kebijakan, dan persetujuan manusia eksplisit.
Perbaiki token Turnstile yang tidak valid dengan memeriksa masa berlaku, kunci situs, aksi, cdata, status browser, verifikasi server, dan ulang kali CapSolver yang dibatasi.

Bangun alur kerja MCP Cloudflare Turnstile yang berdasarkan kebijakan dengan CapSolver, pengulangan terbatas, log yang telah dihapus bagian tertentu, pemeriksaan sesi, dan validasi hasil.
