
Adélia Cruz
Neural Network Developer

AntiCloudflareTask com um proxy estático ou sticky.O monitoramento confiável de estoque em comércio eletrônico é um problema de evidência, não apenas um problema de recuperação de páginas. Uma página de produto pode mostrar "em estoque" enquanto um tamanho específico está indisponível, uma API de mercado pode estar atrasada em relação a um feed de comerciante e um desafio da Cloudflare pode substituir a página esperada por uma resposta intersticial. O fluxo correto é primeiro API, consciente de variantes e consistente em sessão. Ele usa feeds oficiais quando disponíveis, registra observações estruturadas de estoque e invoca o CapSolver apenas quando um desafio da Cloudflare suportado interrompe uma fallback de navegador autorizado. Este guia explica o modelo de dados, o fluxo de recuperação de desafio, os requisitos de proxy estático e agente de usuário, a transferência de cookies, a detecção de mudanças no estoque, os controles de alerta e os limites de conformidade para operações de varejo, inteligência de catálogo e monitoramento de disponibilidade aprovado.
O monitoramento de estoque deve responder a uma pergunta operacional específica. Exemplos comuns incluem:
Evite um objetivo vago como "monitore este produto". Defina o identificador do produto, variante, região, contexto de entrega, fonte e condição de alerta.
inventory_job = {
"canonical_product_id": "catalog-7821",
"gtin": "0099999999999",
"variant": {
"color": "preto",
"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"],
}
O blog de comércio da CapSolver aborda fluxos de comércio relacionados, e a FAQ de raspagem da CapSolver explica considerações operacionais para coleta de dados públicos permitidos.
Fontes oficiais geralmente são mais estáveis e fáceis de auditoriar. Use feeds de comerciantes, APIs de vendedores, pontos finais de estoque de mercado e fornecedores de catálogo licenciados antes de ler páginas voltadas para o comprador.
A documentação da API de Navegação do eBay suporta busca de itens por palavra-chave, categoria, ePID, GTIN, condição e outros filtros. Para lojas que publicam páginas de produto estruturadas, Schema.org Offer define campos como availability, price, priceCurrency, seller e quantidade elegível. A documentação da estrutura de dados de produto do Google explica como dados de oferta e disponibilidade podem aparecer na marcação de produto.
| Fonte | Papel recomendado | Principal força | Principal limitação |
|---|---|---|---|
| Feed de estoque do comerciante | Principal para catálogo próprio | Dados diretos de SKU e quantidade | Limitado à sua relação comercial |
| API de mercado | Principal para listagens de mercado aprovadas | Identificadores e filtros estruturados | Limites e campos específicos do mercado |
| Fornecedor licenciado | Normalização transmercado | Esquema consistente | Custo de licença e cobertura |
| Página pública autorizada | Validação e cobertura de lacunas | Reflete o estado voltado para o comprador | Mudanças de layout e validação de tráfego |
A coleta por navegador deve validar ou complementar uma lacuna de dados conhecida, não substituir uma fonte oficial disponível.
Um campo in_stock: true genérico não é suficiente. Preserve variante, canal, mercado, vendedor e evidência.
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()
)
Use um vocabulário controlado para disponibilidade:
VALID_AVAILABILITY = {
"IN_STOCK",
"OUT_OF_STOCK",
"PREORDER",
"BACKORDER",
"LIMITED",
"UNKNOWN",
}
Se a página disser apenas "disponível", registre a quantidade como None. Não infira um valor numérico.
O guia de coleta de dados da web com Python da CapSolver fornece contexto de implementação, enquanto o glossário da CapSolver pode ajudar as equipes a padronizar termos.
O parser deve verificar a identidade da página antes de ler os dados de estoque. Uma página de desafio pode retornar HTTP 200 e ainda não conter os elementos de produto esperados.
CHALLENGE_TITLES = {
"apenas um momento...",
"atenção necessária!",
}
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"
Trate esses marcadores como sinais de roteamento, não como prova universal. Mantenha fixtures específicos do alvo e teste-os contra páginas às quais você tem autorização para acessar.
A página do produto da Cloudflare da CapSolver descreve a tarefa de desafio suportada, e o blog da Cloudflare da CapSolver contém contexto de solução de problemas.
A documentação oficial de desafio da Cloudflare da CapSolver define AntiCloudflareTask.
| Campo | Obrigatório | Uso para monitoramento de estoque |
|---|---|---|
type |
Sim | Fixo como AntiCloudflareTask |
websiteURL |
Sim | URL exata aprovada de produto ou listagem |
proxy |
Sim | Proxy estático ou sticky usado pelo navegador |
userAgent |
Não | Agente de usuário do Chrome suportado exato do navegador |
html |
Não | HTML intersticial fresco quando necessário |
A solução pode incluir um cookie cf_clearance, token e agente de usuário. Esses valores são materiais de sessão de curta duração. Eles devem ser consumidos pelo runtime de monitoramento, não armazenados em um data warehouse de análise.
A documentação de desafios da Cloudflare explica o propósito e os tipos de mecanismos de desafio. Capacidade técnica não concede permissão de acesso, então a política de fonte permanece como regra controladora.
Não expor credenciais de proxy a analistas, modelos, logs ou alertas. Resolva um perfil dentro de código confiável.
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("Fonte de estoque não aprovada")
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("Solução de desafio não incluiu clearance")
return {
"cookies": cookies,
"user_agent": solution.get("userAgent") or chrome_user_agent,
"proxy_profile": profile,
}
Use um proxy estático ou sticky. Não rotacione a identidade de rede entre navegação inicial, resolução e recuperação da página.
Crie o contexto do Playwright com o proxy e agente de usuário aprovados, capture o estado do desafio, obtenha a solução e aplique os cookies dentro de um contexto compatível.
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
Formatos diferentes de proxy exigem campos diferentes no Playwright. Parseie servidor, nome de usuário e senha do proxy dentro do adaptador do cofre quando necessário.
Prefira JSON-LD ou contratos de página estáveis em vez de texto de apresentação.
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
Normalize a disponibilidade sem inventar a quantidade:
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
Armazene um hash da evidência relevante e versão do parser. Isso torna os alertas reproduzíveis sem reter conteúdo de página desnecessário.
Alerte sobre transições, não sobre snapshots repetidos.
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"
Exija duas observações quando a fonte for ruidosa:
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)
A segunda amostra reduz alertas causados por um erro temporário do parser ou estado da página. Ajuste a regra de acordo com a frequência de atualização da fonte.
Um evento de desafio é um sinal de infraestrutura. Não é uma mudança de estoque.
| Métrica | Significado | Destino do alerta |
|---|---|---|
inventory_restock_total |
Transição confirmada de indisponível para disponível | Operações de comércio |
inventory_unknown_total |
Parser não conseguiu determinar a disponibilidade | Fila de qualidade de dados |
challenge_encounter_total |
Página aprovada apresentou um desafio | Operações de automação |
challenge_recovery_success |
Recuperação concluída e página de produto retornou | Painel de confiabilidade |
challenge_loop_total |
Página permaneceu desafiada após recuperação | Revisão do operador |
Nunca classifique uma página de desafio, erro HTTP ou seletor vazio como OUT_OF_STOCK.
A Perguntas frequentes sobre erros do CapSolver fornece orientações diagnósticas, e a blog de automação do CapSolver aborda padrões de recuperação relacionados.
Código Bônus: Use o código WEBS no Painel do CapSolver para obter um bônus adicional de 5% em cada recarga.
| Controle | Implementação recomendada |
|---|---|
| Permissão de origem | Registro de aprovação por host e limite de propósito |
| Prioridade de origem | Feed ou API antes de fallback no navegador |
| Proxy | Servidor-resolvido estático ou com perfil fixo |
| Agente do usuário | Identidade do Chrome suportada, mesma através da recuperação |
| Cookies | Armazenamento criptografado de curta duração; sem retenção de análise |
| Tentativa de recuperação | Uma tentativa de recuperação, depois revisão do operador |
| Limite de taxa | Quotas específicas por fonte com backoff e jitter |
| Alertas | Notificação somente leitura por padrão |
| Ação de alto impacto | Confirmação explícita antes de reserva ou compra |
Use a Perguntas frequentes sobre resolução de CAPTCHA do CapSolver para entender o fluxo de tarefas e a página de produtos do CapSolver para revisar as categorias de soluções suportadas.
Monitore apenas fontes às quais você tem autorização. Siga licenças de API de mercado, termos de comerciantes, limites de taxa, requisitos de privacidade e contratos de dados de estoque. Não use recuperação de desafio para acessar contas privadas, painéis de vendedores restritos, registros de compradores ou estoque não público. Mantenha o sistema somente leitura, a menos que um serviço aprovado separado trate reservas ou checkout com consentimento humano explícito.
A recuperação do desafio Cloudflare pode tornar o monitoramento de estoque de e-commerce mais confiável, mas apenas quando está dentro de uma pipeline de dados orientada a API, consciente de variantes e controlada por políticas. O monitor deve validar a identidade da página, preservar a consistência do proxy e do agente do usuário, consumir cookies de liberação brevemente, analisar evidências de disponibilidade estruturada e separar falhas de infraestrutura de mudanças reais no estoque.
Inicie um fluxo aprovado com o CapSolver, teste-o contra uma fonte controlada e adicione retenção de evidências, limites de taxa e revisão do operador antes de escalar.
Não. Prefira feeds de comerciantes, APIs de mercado, APIs de vendedores e fontes de dados licenciadas. Use um navegador autorizado apenas para lacunas permitidas ou validação voltada ao comprador.
Use a AntiCloudflareTask documentada com a URL exata do alvo e um proxy estático ou fixo. Campos opcionais incluem o agente do usuário do Chrome suportado pelo navegador e o HTML do desafio recente.
Não. Um desafio, página de erro ou seletor ausente é um estado de infraestrutura ou analisador. Registre UNKNOWN e o direcione separadamente das transições de estoque.
Mantenha-os apenas em armazenamento criptografado de curta duração. Não os coloque no contexto do modelo, tabelas de análise, alertas ou logs de longo prazo.
Mantenha o monitoramento somente leitura por padrão. Reservas, checkout e compra exigem um serviço aprovado separado, validação de preço fresco, limites de política e confirmação humana explícita.
Corrigir um token de Turnstile inválido verificando expiração, chave do site, ação, cdata, estado do navegador, verificação do servidor e retries limitados do CapSolver.

Construa um fluxo de trabalho MCP Cloudflare Turnstile com gate de política, retries limitados, logs redatados, verificações de sessão e validação de resultados.
