Seguir el precio y el stock de miles de productos en tiendas online es sencillo hasta que aparece el primer CAPTCHA y tu scraper se detiene. La respuesta no es dejar de automatizar, sino resolver ese CAPTCHA de forma programática: CaptchaAI devuelve el token de reCAPTCHA v2, reCAPTCHA v3 o Cloudflare Turnstile en segundos y tu monitor de precios continúa sin intervención manual.
Este es el flujo que usa, por ejemplo, una agencia que vigila precios en Amazon.es o en marketplaces regionales tipo MercadoLibre para su cliente de electrónica: comprobaciones periódicas, detección de cambios de precio y alertas de reposición, con el captcha solver integrado dentro del ciclo de scraping. Respeta siempre los términos de servicio del sitio y la normativa de protección de datos aplicable. Abajo tienes el código en Python listo para adaptar, además de las cadencias recomendadas y una guía de resolución de problemas.
Qué CAPTCHA aparece en cada plataforma de retail
Antes de escribir una línea de código conviene saber qué te vas a encontrar. Estos son los tipos más habituales por plataforma y qué dato suele interesar extraer:
| Plataforma | Tipo de CAPTCHA | Qué lo activa | Datos objetivo |
|---|---|---|---|
| Amazon | reCAPTCHA v2 | acceso de alto volumen | precio, stock, reseñas |
| Walmart | reCAPTCHA v3 + Cloudflare | detección de bots | inventario, precios |
| Best Buy | reCAPTCHA v2 | validación de "añadir al carrito" | stock, precios |
| Target | Cloudflare Turnstile | acceso automatizado | disponibilidad |
| Tiendas Shopify | Cloudflare Turnstile | límite de solicitudes | datos de producto |
| eBay | reCAPTCHA v2 | búsqueda y acceso a listados | listados, precios |
CaptchaAI resuelve los tres tipos que dominan esta tabla —reCAPTCHA v2, reCAPTCHA v3 y Cloudflare Turnstile—, así que el mismo monitor cubre casi todo el retail sin cambiar de proveedor.
Qué necesitas antes de empezar
Reúne estas piezas y el resto es adaptar los selectores a cada tienda:
- una clave de API de CaptchaAI (el
YOUR_API_KEYdel código); - Python con
requestsyBeautifulSoupinstalados; - una salida de red autorizada por producto o categoría;
- una cadencia de comprobación acorde a cada tipo de producto (la tienes justo debajo).
Con qué frecuencia comprobar cada tipo de producto
La cadencia decide cuántos CAPTCHA vas a disparar y qué salida de red te conviene. Productos volátiles piden intervalos cortos; los estables toleran comprobaciones espaciadas y salidas más baratas:
| Tipo de producto | Frecuencia de comprobación | Salida de red recomendada |
|---|---|---|
| Electrónica | cada 30 minutos | residencial rotativa |
| Alimentación | cada 4 horas | residencial rotativa |
| Moda | cada 2 horas | residencial |
| Lanzamientos limitados | cada 1 minuto | red móvil autorizada |
| Artículos para el hogar | cada 6 horas | residencial |
| Productos básicos | cada 12 horas | centro de datos (a menudo suficiente) |
Cómo monitorear un producto con manejo de CAPTCHA
El núcleo es una clase reutilizable: pide la página, detecta si vino un CAPTCHA, lo resuelve con CaptchaAI, reenvía el token y luego extrae precio, disponibilidad y estado de stock. La misma clase sirve para una comprobación puntual o para un bucle continuo.
import requests
import time
import re
import json
from datetime import datetime
from bs4 import BeautifulSoup
CAPTCHAAI_KEY = "YOUR_API_KEY"
CAPTCHAAI_URL = "https://ocr.captchaai.com"
def solve_captcha(method, sitekey, pageurl, **kwargs):
data = {
"key": CAPTCHAAI_KEY, "method": method,
"googlekey": sitekey, "pageurl": pageurl, "json": 1,
}
data.update(kwargs)
resp = requests.post(f"{CAPTCHAAI_URL}/in.php", data=data)
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,
})
r = result.json()
if r["request"] != "CAPCHA_NOT_READY":
return r["request"]
raise TimeoutError("Timeout")
class RetailMonitor:
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",
"Accept": "text/html,application/xhtml+xml,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.9",
})
def check_product(self, url):
"""Check single product's price and availability."""
resp = self.session.get(url, timeout=30)
# Handle CAPTCHA
if self._has_captcha(resp.text):
resp = self._solve_and_retry(resp.text, url)
soup = BeautifulSoup(resp.text, "html.parser")
return {
"url": url,
"title": self._text(soup, "h1, .product-title, #productTitle"),
"price": self._text(soup, ".price, .a-price .a-offscreen, .prod-price"),
"availability": self._text(soup, "#availability, .stock-status, .fulfillment"),
"in_stock": self._check_stock(soup),
"timestamp": datetime.now().isoformat(),
}
def monitor_products(self, product_urls, interval_sec=1800):
"""Continuously monitor products for changes."""
history = {}
while True:
for url in product_urls:
try:
current = self.check_product(url)
# Check for changes
prev = history.get(url)
if prev:
changes = self._detect_changes(prev, current)
if changes:
self._alert(current["title"], changes)
history[url] = current
time.sleep(3)
except Exception as e:
print(f"Error checking {url}: {e}")
print(f"Cycle complete: {len(product_urls)} products checked")
time.sleep(interval_sec)
def track_prices(self, product_urls, output_file="prices.json"):
"""Single price check across all products."""
results = []
for url in product_urls:
try:
data = self.check_product(url)
results.append(data)
time.sleep(3)
except Exception as e:
results.append({"url": url, "error": str(e)})
with open(output_file, "w") as f:
json.dump(results, f, indent=2)
print(f"Tracked {len(results)} products → {output_file}")
return results
def _has_captcha(self, html):
return any(tag in html.lower() for tag in [
'data-sitekey', 'g-recaptcha', 'cf-turnstile', 'captcha',
])
def _solve_and_retry(self, html, url):
match = re.search(r'data-sitekey="([^"]+)"', html)
if not match:
return self.session.get(url)
sitekey = match.group(1)
if 'cf-turnstile' in html:
token = solve_captcha("turnstile", sitekey, url)
return self.session.post(url, data={"cf-turnstile-response": token})
else:
token = solve_captcha("userrecaptcha", sitekey, url)
return self.session.post(url, data={"g-recaptcha-response": token})
def _text(self, soup, selector):
el = soup.select_one(selector)
return el.get_text(strip=True) if el else ""
def _check_stock(self, soup):
stock_el = soup.select_one("#availability, .stock-status")
if stock_el:
text = stock_el.get_text(strip=True).lower()
return "in stock" in text or "available" in text
return None
def _detect_changes(self, prev, current):
changes = []
if prev["price"] != current["price"]:
changes.append(f"Price: {prev['price']} → {current['price']}")
if prev["in_stock"] != current["in_stock"]:
status = "In Stock" if current["in_stock"] else "Out of Stock"
changes.append(f"Stock: → {status}")
return changes
def _alert(self, title, changes):
print(f"ALERT [{title}]: {', '.join(changes)}")
# Usage
monitor = RetailMonitor(
proxy="http://user:pass@residential.proxy.com:5000"
)
products = [
"https://store.example.com/product/abc123",
"https://store.example.com/product/def456",
"https://store.example.com/product/ghi789",
]
# One-time price check
results = monitor.track_prices(products)
# Or continuous monitoring (every 30 min)
# monitor.monitor_products(products, interval_sec=1800)
El método _solve_and_retry elige el method correcto según lo que detecta en el HTML: turnstile cuando encuentra cf-turnstile y userrecaptcha para reCAPTCHA. El token que devuelve CaptchaAI se reenvía en el campo esperado (cf-turnstile-response o g-recaptcha-response) y la sesión sigue su curso.
Escaneo de stock de toda una categoría
Para vigilar un catálogo completo en lugar de URLs sueltas, recorre las páginas de una categoría y resuelve el CAPTCHA solo cuando aparezca. El bucle se detiene solo en cuanto una página deja de devolver artículos:
def scan_category(base_url, category, max_pages=20):
"""Scan an entire product category for stock status."""
monitor = RetailMonitor(
proxy="http://user:pass@residential.proxy.com:5000"
)
all_products = []
for page in range(1, max_pages + 1):
url = f"{base_url}/{category}?page={page}"
resp = monitor.session.get(url, timeout=30)
if monitor._has_captcha(resp.text):
resp = monitor._solve_and_retry(resp.text, url)
soup = BeautifulSoup(resp.text, "html.parser")
items = soup.select(".product-card, .s-result-item")
if not items:
break
for item in items:
all_products.append({
"name": monitor._text(item, ".product-name, .a-text-normal"),
"price": monitor._text(item, ".price, .a-price"),
"stock": monitor._text(item, ".stock, .a-color-success"),
"url": item.select_one("a")["href"] if item.select_one("a") else "",
})
time.sleep(3)
return all_products
Comparar precios entre tiendas competidoras
El mismo patrón resuelve el caso de negocio más pedido: cuánto cuesta un producto en cada retailer. Se busca el término en cada tienda, se resuelve el CAPTCHA si aparece y se ordenan los resultados por precio.
def compare_product_across_stores(product_name, stores):
"""Compare prices across retailers for the same product."""
results = []
for store in stores:
monitor = RetailMonitor(proxy=store.get("proxy"))
search_url = f"{store['base_url']}/search?q={product_name}"
try:
resp = monitor.session.get(search_url, timeout=30)
if monitor._has_captcha(resp.text):
resp = monitor._solve_and_retry(resp.text, search_url)
soup = BeautifulSoup(resp.text, "html.parser")
first_result = soup.select_one(".product-card, .s-result-item")
if first_result:
results.append({
"store": store["name"],
"price": monitor._text(first_result, ".price"),
"in_stock": "in stock" in first_result.get_text().lower(),
})
except Exception as e:
results.append({"store": store["name"], "error": str(e)})
time.sleep(5)
results.sort(key=lambda x: x.get("price", "zzzz"))
return results
Solución de problemas frecuentes
| Problema | Causa | Solución |
|---|---|---|
| Aparece un CAPTCHA en cada página de producto | IP marcada o frecuencia excedida | aumenta el retraso y rota las IP de salida |
| Precio incorrecto al extraer | precios dinámicos renderizados por JS | usa Selenium o Puppeteer en su lugar |
| Página de "verificación de robot" | detección de bots tipo Amazon | usa headers realistas y salida de red residencial autorizada |
| El stock aparece como "no disponible" | disponibilidad restringida por región | haz coincidir la salida de red con la región objetivo |
| Faltan datos del producto | cambió la estructura de la página | actualiza los selectores CSS |
Preguntas frecuentes
¿Qué plan de CaptchaAI necesito para monitorear un catálogo grande?
Depende de cuántas comprobaciones quieras lanzar en paralelo, no del número de productos. CaptchaAI factura por thread concurrente con resoluciones ilimitadas: el plan BASIC ($15/mes, 5 threads) sirve para un monitor pequeño, mientras que ADVANCE ($90/mes, 50 threads) mantiene decenas de escaneos simultáneos sin coste por resolución.
¿CaptchaAI resuelve el CAPTCHA de Amazon o el de Cloudflare Turnstile?
Sí. Resuelve reCAPTCHA v2, reCAPTCHA v3, Cloudflare Turnstile y Cloudflare Challenge, que son los tipos que verás en Amazon, Walmart, Target y tiendas Shopify. No resuelve hCaptcha ni FunCaptcha, así que si un retailer los usa necesitarás otro enfoque.
¿Es legal monitorear precios de la competencia?
Los precios públicos suelen ser consultables, pero cada sitio tiene sus términos de servicio y aplica normativa de protección de datos (GDPR y LOPDGDD en España, LFPDPPP en México). Limita el scraping a datos públicos, respeta esos términos y no recopiles información personal.
¿Cómo evito que me bloqueen tras unas pocas páginas?
Espacia las solicitudes, rota las IP de salida y usa headers realistas. Comprobar cada producto cada 30–60 minutos es un ritmo seguro para la mayoría de retailers; bajar a 1–5 minutos funciona, pero dispara más CAPTCHA y exige más salidas de red.
Guías relacionadas
- Salida de red residencial rotativa
- Sesiones fijas frente a rotativas
- Monitoreo de la cadena de suministro
Monitorea el inventario minorista en tiempo real: obtén tu clave de CaptchaAI y añade manejo automatizado de CAPTCHA a tu pipeline.