¿Quieres seguir el precio de un listado sin que un reCAPTCHA v2 detenga tu scraper a mitad de la consulta? La respuesta corta: resuelve el CAPTCHA por API en cuanto aparece, inyecta el token en la misma solicitud y sigue adelante. Las plataformas de subastas —desde casas de pujas internacionales hasta marketplaces tipo MercadoLibre— protegen la búsqueda, las páginas de detalle y el historial de pujas con reCAPTCHA v2, y lo disparan justo cuando más molesta: durante consultas rápidas y recargas frecuentes. Esta guía muestra cómo mantener un seguimiento estable resolviendo esos desafíos de forma automática, con ejemplos listos para copiar en Python y JavaScript.
Por qué aparecen los CAPTCHA en los sitios de subastas
Antes de escribir código conviene saber dónde salta el desafío. En la mayoría de las plataformas, el reCAPTCHA v2 se activa por el ritmo y el volumen de las solicitudes, no por la acción en sí. Estos son los puntos más habituales:
| Acción | Tipo de CAPTCHA | Qué lo dispara |
|---|---|---|
| Buscar o navegar listados | reCAPTCHA v2 | Búsquedas rápidas y secuenciales |
| Ver el detalle de un listado | reCAPTCHA v2 | Mucho volumen desde la misma IP |
| Consultar el historial de pujas | reCAPTCHA v2 | Recargas repetidas de la página de detalle |
| Navegar por categorías | Cloudflare Turnstile | Velocidad de navegación propia de un bot |
| Páginas de alerta de precio | reCAPTCHA v2 | Actualizaciones frecuentes |
Ten en cuenta que un mismo sitio puede combinar reCAPTCHA v2 en la búsqueda con Cloudflare Turnstile en la navegación por categorías. Conviene detectar ambos y resolver el que corresponda en cada paso.
Monitorear subastas resolviendo el reCAPTCHA v2 en Python
El patrón es sencillo: haces la solicitud normal, compruebas si la respuesta trae un CAPTCHA, extraes el sitekey, lo envías a CaptchaAI y reenvías la petición con el token g-recaptcha-response. La clase siguiente encapsula ese flujo y añade un seguimiento continuo de las pujas.
import requests
import time
import re
from datetime import datetime
class AuctionMonitor:
def __init__(self, api_key):
self.api_key = api_key
self.session = requests.Session()
self.session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
})
def search_listings(self, auction_url, query, category=None):
"""Search auction listings, solving CAPTCHAs when triggered."""
params = {"q": query}
if category:
params["category"] = category
response = self.session.get(
f"{auction_url}/search", params=params
)
if self._has_captcha(response.text):
site_key = self._extract_site_key(response.text)
token = self._solve_recaptcha(site_key, f"{auction_url}/search")
response = self.session.post(
f"{auction_url}/search",
data={**params, "g-recaptcha-response": token}
)
return self._parse_listings(response.text)
def monitor_listing(self, auction_url, listing_id):
"""Get current bid and listing details."""
url = f"{auction_url}/item/{listing_id}"
response = self.session.get(url)
if self._has_captcha(response.text):
site_key = self._extract_site_key(response.text)
token = self._solve_recaptcha(site_key, url)
response = self.session.post(url, data={
"g-recaptcha-response": token
})
return self._parse_listing_detail(response.text)
def track_bids(self, auction_url, listing_ids, interval=60):
"""Track bid changes across multiple listings."""
history = {lid: [] for lid in listing_ids}
while True:
for listing_id in listing_ids:
try:
detail = self.monitor_listing(auction_url, listing_id)
previous = history[listing_id]
if previous and detail["current_bid"] != previous[-1]["current_bid"]:
print(f"Bid change on {listing_id}: "
f"${previous[-1]['current_bid']} → ${detail['current_bid']}")
history[listing_id].append(detail)
except Exception as e:
print(f"Error checking {listing_id}: {e}")
time.sleep(interval)
def _has_captcha(self, html):
return "g-recaptcha" in html or "recaptcha" in html.lower()
def _extract_site_key(self, html):
match = re.search(r'data-sitekey="([^"]+)"', html)
if match:
return match.group(1)
match = re.search(r"sitekey['\"]?\s*[:=]\s*['\"]([^'\"]+)", html)
if match:
return match.group(1)
raise ValueError("Could not find reCAPTCHA site key")
def _solve_recaptcha(self, site_key, page_url):
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": self.api_key,
"method": "userrecaptcha",
"googlekey": site_key,
"pageurl": page_url,
"json": 1
})
task_id = resp.json()["request"]
for _ in range(60):
time.sleep(3)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": self.api_key,
"action": "get",
"id": task_id,
"json": 1
})
data = result.json()
if data["status"] == 1:
return data["request"]
raise TimeoutError("reCAPTCHA solve timed out")
def _parse_listings(self, html):
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, "html.parser")
def text_or_none(node):
return node.text.strip() if node and node.text else None
def attr_or_none(node, attr):
return node.get(attr) if node else None
listings = []
for item in soup.select(".listing-item, .auction-item"):
listings.append({
"title": text_or_none(item.select_one(".title")),
"current_bid": text_or_none(item.select_one(".price, .bid")),
"time_left": text_or_none(item.select_one(".time-left")),
"url": attr_or_none(item.select_one("a"), "href")
})
return listings
def _parse_listing_detail(self, html):
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, "html.parser")
def text_or_none(node):
return node.text.strip() if node and node.text else None
return {
"title": text_or_none(soup.select_one("h1, .item-title")),
"current_bid": text_or_none(soup.select_one(".current-bid, .price")),
"bid_count": text_or_none(soup.select_one(".bid-count")),
"time_left": text_or_none(soup.select_one(".time-remaining")),
"checked_at": datetime.now().isoformat()
}
# Usage
monitor = AuctionMonitor("YOUR_API_KEY")
listings = monitor.search_listings(
"https://auctions.example.com",
"vintage electronics",
category="collectibles"
)
El token de reCAPTCHA v2 tiene una vida útil de unos dos minutos, así que resuélvelo justo antes de reenviar la solicitud protegida. Reutilizar la misma requests.Session() conserva las cookies y reduce cuántas veces vuelve a aparecer el desafío.
Sistema de alertas de precio en JavaScript
Si prefieres Node.js o quieres integrar el monitor en un backend JavaScript, el mismo patrón funciona con fetch. Esta versión mantiene una lista de vigilancia y avisa cuando una puja se acerca al límite que definiste.
class AuctionTracker {
constructor(apiKey) {
this.apiKey = apiKey;
this.watchList = new Map();
}
addWatch(listingId, url, maxPrice) {
this.watchList.set(listingId, { url, maxPrice, history: [] });
}
async checkAll() {
const alerts = [];
for (const [id, watch] of this.watchList) {
try {
const detail = await this.fetchListing(watch.url);
watch.history.push(detail);
const price = parseFloat(detail.currentBid.replace(/[^0-9.]/g, ''));
if (price >= watch.maxPrice * 0.9) {
alerts.push({
listing: id,
price,
threshold: watch.maxPrice,
message: `Price approaching limit: $${price} / $${watch.maxPrice}`
});
}
} catch (error) {
alerts.push({ listing: id, error: error.message });
}
}
return alerts;
}
async fetchListing(url) {
const response = await fetch(url);
const html = await response.text();
if (html.includes('g-recaptcha')) {
return this.solveAndFetch(url, html);
}
return this.parseDetail(html);
}
async solveAndFetch(url, html) {
const siteKeyMatch = html.match(/data-sitekey="([^"]+)"/);
if (!siteKeyMatch) throw new Error('No reCAPTCHA site key found');
const submitResp = await fetch('https://ocr.captchaai.com/in.php', {
method: 'POST',
body: new URLSearchParams({
key: this.apiKey,
method: 'userrecaptcha',
googlekey: siteKeyMatch[1],
pageurl: url,
json: '1'
})
});
const { request: taskId } = await submitResp.json();
for (let i = 0; i < 60; i++) {
await new Promise(r => setTimeout(r, 3000));
const result = await fetch(
`https://ocr.captchaai.com/res.php?key=${this.apiKey}&action=get&id=${taskId}&json=1`
);
const data = await result.json();
if (data.status === 1) {
// Resubmit with token
const response = await fetch(url, {
method: 'POST',
body: new URLSearchParams({ 'g-recaptcha-response': data.request })
});
return this.parseDetail(await response.text());
}
}
throw new Error('reCAPTCHA solve timed out');
}
parseDetail(html) {
// Parse auction listing details from HTML
return {
currentBid: html.match(/current.?bid[^>]*>([^<]+)/i)?.[1]?.trim(),
bidCount: html.match(/(\d+)\s*bids?/i)?.[1],
timeLeft: html.match(/time.?(?:left|remaining)[^>]*>([^<]+)/i)?.[1]?.trim(),
checkedAt: new Date().toISOString()
};
}
}
// Usage
const tracker = new AuctionTracker('YOUR_API_KEY');
tracker.addWatch('item-123', 'https://auctions.example.com/item/123', 500);
tracker.addWatch('item-456', 'https://auctions.example.com/item/456', 200);
const alerts = await tracker.checkAll();
Cada cuánto conviene consultar los listados
La frecuencia de consulta define casi todo: cuanto más rápido sondeas, más CAPTCHA verás. Elige el intervalo según la urgencia real del caso.
| Frecuencia de consulta | Caso de uso | CAPTCHA esperados |
|---|---|---|
| Cada 30 segundos | Pujas de último minuto | Muchos: reparte la carga entre varias salidas |
| Cada 5 minutos | Seguimiento activo de una subasta | Moderados |
| Cada 15 minutos | Vigilancia de una lista de seguimiento | Pocos |
| Cada hora | Investigación de precios a largo plazo | Mínimos |
Aquí es donde el modelo de precios importa. CaptchaAI factura por threads concurrentes con resoluciones ilimitadas, no por CAPTCHA resuelto: un thread es un desafío en curso y, en cuanto termina, queda libre para el siguiente. Para una agencia o un freelance que factura en una moneda local volátil, un costo mensual predecible en USD suele encajar mejor que el pago por resolución. Si sigues unas pocas subastas, el plan BASIC ($15/mes, 5 threads) basta; cuando vigilas cientos de listados en paralelo, STANDARD ($30/mes, 15 threads) o ADVANCE ($90/mes, 50 threads) amplían los threads disponibles.
Cómo reducir la frecuencia de CAPTCHA
Menos desafíos significan menos latencia y menos gasto de threads. Estas técnicas atacan el origen del problema: el patrón de las solicitudes.
| Técnica | Efecto |
|---|---|
| Reutilizar las cookies de sesión | Conserva el estado autenticado |
| Repartir la carga entre salidas de red autorizadas | Distribuye las solicitudes entre varias IP |
| Aleatorizar los intervalos entre solicitudes | Evita patrones periódicos fáciles de detectar |
| Usar cuentas autenticadas | Reduce el umbral que dispara el CAPTCHA |
Resolución de problemas frecuentes
| Problema | Causa | Solución |
|---|---|---|
| CAPTCHA en cada solicitud | No se conserva la sesión | Reutiliza requests.Session() |
| Token reCAPTCHA rechazado | Token caducado (vive 2 minutos) | Resuélvelo justo antes de enviar |
| La página de listados devuelve 0 resultados | El CAPTCHA filtró los resultados en silencio | Comprueba si hay elementos CAPTCHA ocultos |
| IP bloqueada tras muchos CAPTCHA | Límite de solicitudes superado | Reparte la carga y aumenta los intervalos |
Preguntas frecuentes
¿Qué plan de CaptchaAI necesito para vigilar varias subastas a la vez?
Depende de cuántos listados sigas en paralelo. CaptchaAI factura por threads concurrentes con resoluciones ilimitadas: el plan BASIC ($15/mes, 5 threads) cubre un seguimiento modesto, y STANDARD ($30/mes, 15 threads) o ADVANCE ($90/mes, 50 threads) amplían los threads cuando rastreas cientos de listados a la vez.
¿Cómo reduzco los bloqueos de IP al rastrear un sitio de subastas?
Reutiliza la sesión, aleatoriza los intervalos entre solicitudes y reparte la carga entre varias salidas de red autorizadas. El bloqueo casi siempre viene del ritmo de las solicitudes, no de resolver el CAPTCHA.
¿CaptchaAI resuelve también el Cloudflare Turnstile que aparece al navegar por categorías?
Sí. Además del reCAPTCHA v2 de la búsqueda y las páginas de detalle, CaptchaAI resuelve Cloudflare Turnstile, así que puedes cubrir ambos desafíos con la misma integración.
¿Es legal monitorear precios de subastas online?
Depende del sitio. Revisa siempre los términos de servicio de la plataforma y la normativa de protección de datos aplicable —por ejemplo, el RGPD y la LOPDGDD en España o la LFPDPPP en México— antes de automatizar el seguimiento.
Artículos relacionados
- Resolver reCAPTCHA v2 con callback vía API
- reCAPTCHA v2 y Turnstile en un mismo sitio
- Cómo funciona el mecanismo de callback de reCAPTCHA v2
Empieza a seguir subastas sin interrupciones: crea tu cuenta en CaptchaAI y deja que los reCAPTCHA v2 se resuelvan solos mientras tu monitor trabaja.