Nota legal: Este artículo cubre el scraping de datos públicos de búsqueda de Amazon. En EE.UU., la Computer Fraud and Abuse Act (CFAA) puede aplicar al acceso no autorizado a sistemas protegidos. En la UE, el RGPD (GDPR) regula el tratamiento de datos personales. Recomendamos rastrear únicamente listados propios o datos públicos, respetar robots.txt y los Términos de Servicio de Amazon, y considerar la SP-API de Amazon para acceso oficial cuando sea posible. Esta guía es educativa y no constituye asesoría legal.
El seguimiento de rankings de palabras clave en Amazon con proxies es una técnica esencial para vendedores y desarrolladores de marketplace SEO que necesitan monitorizar la posición orgánica de sus productos a lo largo del tiempo. A diferencia de Google, Amazon tiene su propio motor de búsqueda con reglas distintas, y sin proxies residenciales adecuados, cualquier intento de automatización se bloquea en cuestión de minutos.
En esta guía verás cómo construir un amazon keyword rank tracker funcional en Python, desde el parsing del SERP hasta la configuración de proxies con geo-targeting por marketplace. Incluye código ejecutable, manejo de errores y consejos de producción.
Por qué Amazon es un SERP diferente a Google
Amazon no es solo un buscador: es un motor de compra. Su algoritmo de ranking, conocido como A9, prioriza la relevancia comercial y la velocidad de ventas (sales velocity) sobre factores puramente informativos. Esto significa que un producto con más conversiones puede superar a otro con mejor optimización on-page para la misma palabra clave.
Para los vendedores, esto implica que la posición orgánica de un ASIN para una keyword concreta puede cambiar diariamente, e incluso varias veces al día. Un amazon keyword rank tracker debe registrar:
- La palabra clave consultada.
- El marketplace (amazon.com, amazon.de, amazon.co.uk, etc.).
- El ASIN objetivo.
- La posición orgánica (no patrocinada).
- La marca de tiempo.
Sin proxies, Amazon detecta patrones de peticiones repetitivas desde una misma IP y responde con CAPTCHAs, páginas vacías o bloqueos HTTP 503. Según la documentación oficial de Amazon SP-API, el acceso programático a datos de catálogo está sujeto a límites estrictos, lo que empuja a muchos desarrolladores a complementar con scraping del SERP público.
Parsing del SERP de Amazon: estructura del HTML
La página de resultados de búsqueda de Amazon tiene una estructura relativamente estable. Cada producto aparece en un contenedor con el atributo data-component-type="s-search-result". Dentro de ese contenedor:
data-asincontiene el identificador del producto (ASIN).- El título del producto está en una etiqueta
h2 > adentro del contenedor. - Los anuncios patrocinados incluyen el texto "Sponsored" en un span con clase
a-color-secondaryo similar. - La posición orgánica se calcula contando solo los resultados que no son Sponsored.
Es importante separar los resultados patrocinados de los orgánicos, porque el vendedor necesita saber su posición orgánica real, no la que paga con publicidad.
Ejemplo de extracción con BeautifulSoup
from bs4 import BeautifulSoup
def parse_amazon_serp(html: str) -> list[dict]:
"""Parsea una página de resultados de Amazon y devuelve lista de productos."""
soup = BeautifulSoup(html, "html.parser")
results = []
organic_position = 0
for div in soup.select('[data-component-type="s-search-result"]'):
asin = div.get("data-asin", "")
if not asin:
continue
# Detectar si es Sponsored
sponsored = False
for span in div.find_all("span", class_="a-color-secondary"):
if "Sponsored" in span.get_text(strip=True):
sponsored = True
break
# Extraer título
title_tag = div.select_one("h2 a")
title = title_tag.get_text(strip=True) if title_tag else ""
if not sponsored:
organic_position += 1
results.append({
"asin": asin,
"title": title,
"sponsored": sponsored,
"organic_position": organic_position if not sponsored else None,
})
return results
Esta función devuelve una lista de diccionarios donde cada producto tiene su ASIN, título, si es patrocinado y su posición orgánica. Los Sponsored no incrementan el contador de posición orgánica.
Por qué necesitas proxies residenciales con geo-targeting
Amazon localiza los resultados de búsqueda por marketplace y por ubicación del cliente. Si haces una petición a amazon.com desde una IP de datacenter en Frankfurt, Amazon puede devolver resultados de amazon.de o mostrar un mensaje de redirección. Esto arruina cualquier tracker de rankings.
Los proxies residenciales resuelven dos problemas simultáneamente:
- Anti-bot: Las IPs residenciales pertenecen a ISPs reales, por lo que Amazon las trata como tráfico de clientes legítimos.
- Localización: Puedes especificar el país de la IP, asegurando que amazon.com devuelva resultados de EE.UU. y amazon.de resultados de Alemania.
Además, Amazon pagína los resultados (típicamente 50-60 productos por página). Para recorrer páginas 1-5 necesitas mantener la misma sesión de navegación, o Amazon puede reiniciar la paginación. Esto se logra con sesiones sticky en el proxy.
Comparación: tipos de proxy para Amazon
| Tipo de proxy | Detectabilidad | Geo-targeting | Sticky sessions | Ideal para Amazon |
|---|---|---|---|---|
| Datacenter | Alta | Limitada | Sí | No recomendado |
| Residencial | Baja | País y ciudad | Sí | Sí, óptimo |
| Móvil | Mínima | País | Sí | Sí, pero más caro |
Configuración de ProxyHat para Amazon
ProxyHat permite geo-targeting por país y sesiones sticky directamente en el nombre de usuario del proxy. Para Amazon:
- amazon.com → usar
-country-US - amazon.de → usar
-country-DE - amazon.co.uk → usar
-country-GB - amazon.fr → usar
-country-FR
Para mantener la paginación estable entre páginas 1-5, usa un -session- ID único por consulta. Cada keyword+marketplace debe tener su propio session ID.
Ejemplo con curl
# Petición a amazon.com con proxy residencial US y sesión sticky
curl -x http://user-country-US-session-kw123:pass@gate.proxyhat.com:8080 \
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" \
-H "Accept-Language: en-US,en;q=0.9" \
"https://www.amazon.com/s?k=bluetooth+earbuds&page=1"
Ejemplo con SOCKS5
# SOCKS5 para mayor compatibilidad con TLS fingerprinting
curl -x socks5://user-country-US-session-kw123:pass@gate.proxyhat.com:1080 \
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" \
"https://www.amazon.com/s?k=bluetooth+earbuds&page=1"
Implementación completa en Python con curl_cffi
curl_cffi es una librería Python que usa el motor TLS de curl, lo que permite imitar el fingerprint de navegadores reales. Esto es crucial para Amazon, que detecta clientes HTTP con fingerprints de Python (como requests o httpx) con relativa facilidad.
import logging
import time
import hashlib
from datetime import datetime, timezone
from curl_cffi import requests as curl_requests
from bs4 import BeautifulSoup
import json
import sqlite3
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
logger = logging.getLogger(__name__)
# Configuración ProxyHat
PROXYHAT_GATEWAY = "gate.proxyhat.com"
PROXYHAT_PORT = 8080
PROXYHAT_USER = "TU_USUARIO"
PROXYHAT_PASS = "TU_PASSWORD"
MARKETPLACES = {
"com": {"country": "US", "base_url": "https://www.amazon.com", "lang": "en-US"},
"de": {"country": "DE", "base_url": "https://www.amazon.de", "lang": "de-DE"},
"co.uk": {"country": "GB", "base_url": "https://www.amazon.co.uk", "lang": "en-GB"},
"fr": {"country": "FR", "base_url": "https://www.amazon.fr", "lang": "fr-FR"},
}
USER_AGENTS = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
]
def build_proxy_url(marketplace: str, session_id: str) -> str:
"""Construye la URL del proxy con geo-targeting y sesión sticky."""
mp = MARKETPLACES[marketplace]
username = f"{PROXYHAT_USER}-country-{mp['country']}-session-{session_id}"
return f"http://{username}:{PROXYHAT_PASS}@{PROXYHAT_GATEWAY}:{PROXYHAT_PORT}"
def fetch_amazon_page(keyword: str, marketplace: str, page: int, session_id: str) -> str | None:
"""Descarga una página del SERP de Amazon con proxy residencial."""
mp = MARKETPLACES[marketplace]
url = f"{mp['base_url']}/s?k={keyword}&page={page}"
proxy = build_proxy_url(marketplace, session_id)
headers = {
"User-Agent": USER_AGENTS[hash(session_id) % len(USER_AGENTS)],
"Accept-Language": mp["lang"],
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
}
try:
response = curl_requests.get(
url,
headers=headers,
proxies={"http": proxy, "https": proxy},
impersonate="chrome120",
timeout=30,
)
if response.status_code == 200:
return response.text
elif response.status_code == 503:
logger.warning(f"503 en {url} — posible bloqueo o CAPTCHA")
return None
else:
logger.warning(f"HTTP {response.status_code} en {url}")
return None
except Exception as e:
logger.error(f"Error fetching {url}: {e}")
return None
def detect_captcha(html: str) -> bool:
"""Detecta si Amazon devolvió una página de CAPTCHA."""
captcha_markers = ["api-services-support@amazon.com", "captcha", "robot"]
html_lower = html.lower()
return any(marker in html_lower for marker in captcha_markers)
def parse_serp(html: str) -> list[dict]:
"""Extrae productos del HTML del SERP de Amazon."""
soup = BeautifulSoup(html, "html.parser")
results = []
organic_position = 0
for div in soup.select('[data-component-type="s-search-result"]'):
asin = div.get("data-asin", "")
if not asin:
continue
sponsored = False
for span in div.find_all("span", class_="a-color-secondary"):
if "Sponsored" in span.get_text(strip=True):
sponsored = True
break
title_tag = div.select_one("h2 a")
title = title_tag.get_text(strip=True) if title_tag else ""
if not sponsored:
organic_position += 1
results.append({
"asin": asin,
"title": title,
"sponsored": sponsored,
"organic_position": organic_position if not sponsored else None,
})
return results
def find_asin_position(results: list[dict], target_asin: str) -> dict:
"""Busca un ASIN objetivo en los resultados y devuelve su posición."""
for r in results:
if r["asin"] == target_asin and not r["sponsored"]:
return {"found": True, "organic_position": r["organic_position"], "sponsored": False}
if r["asin"] == target_asin and r["sponsored"]:
return {"found": True, "organic_position": None, "sponsored": True}
return {"found": False, "organic_position": None, "sponsored": False}
def track_keyword(keyword: str, target_asin: str, marketplace: str, max_pages: int = 5) -> dict:
"""Rastrea la posición de un ASIN para una keyword en un marketplace."""
session_id = hashlib.md5(f"{keyword}:{marketplace}".encode()).hexdigest()[:12]
all_results = []
for page in range(1, max_pages + 1):
html = fetch_amazon_page(keyword, marketplace, page, session_id)
if html is None:
logger.warning(f"No se pudo obtener página {page} para '{keyword}'")
break
if detect_captcha(html):
logger.error(f"CAPTCHA detectado en página {page} — abortando keyword")
break
page_results = parse_serp(html)
if not page_results:
logger.warning(f"Sin resultados en página {page} — posible fin del catálogo")
break
all_results.extend(page_results)
position = find_asin_position(page_results, target_asin)
if position["found"]:
logger.info(f"ASIN {target_asin} encontrado en página {page}, posición orgánica: {position['organic_position']}")
return {
"keyword": keyword,
"marketplace": marketplace,
"asin": target_asin,
"page": page,
"organic_position": position["organic_position"],
"sponsored": position["sponsored"],
"timestamp": datetime.now(timezone.utc).isoformat(),
}
time.sleep(2) # Throttling entre páginas
logger.info(f"ASIN {target_asin} no encontrado en páginas 1-{max_pages} para '{keyword}'")
return {
"keyword": keyword,
"marketplace": marketplace,
"asin": target_asin,
"page": None,
"organic_position": None,
"sponsored": False,
"timestamp": datetime.now(timezone.utc).isoformat(),
}
Almacenamiento en SQLite
def init_db(db_path: str = "rankings.db") -> sqlite3.Connection:
"""Inicializa la base de datos de rankings."""
conn = sqlite3.connect(db_path)
conn.execute("""
CREATE TABLE IF NOT EXISTS keyword_rankings (
id INTEGER PRIMARY KEY AUTOINCREMENT,
keyword TEXT NOT NULL,
marketplace TEXT NOT NULL,
asin TEXT NOT NULL,
page INTEGER,
organic_position INTEGER,
sponsored BOOLEAN DEFAULT 0,
timestamp TEXT NOT NULL,
UNIQUE(keyword, marketplace, asin, timestamp)
)
""")
conn.commit()
return conn
def save_ranking(conn: sqlite3.Connection, ranking: dict) -> None:
"""Guarda un registro de ranking en la base de datos."""
conn.execute("""
INSERT OR REPLACE INTO keyword_rankings
(keyword, marketplace, asin, page, organic_position, sponsored, timestamp)
VALUES (?, ?, ?, ?, ?, ?, ?)
""", (
ranking["keyword"],
ranking["marketplace"],
ranking["asin"],
ranking["page"],
ranking["organic_position"],
ranking["sponsored"],
ranking["timestamp"],
))
conn.commit()
# Uso
if __name__ == "__main__":
conn = init_db()
keywords = ["bluetooth earbuds", "wireless earbuds", "noise cancelling headphones"]
target_asin = "B0CX23V2ZK"
marketplace = "com"
for kw in keywords:
result = track_keyword(kw, target_asin, marketplace, max_pages=5)
save_ranking(conn, result)
time.sleep(3) # Throttling entre keywords
conn.close()
Alternativa con Playwright para JavaScript rendering
A veces Amazon sirve resultados mediante JavaScript dinámico, especialmente en marketplaces menos estables. En esos casos, Playwright es una mejor opción porque renderiza la página completa.
from playwright.sync_api import sync_playwright
def fetch_with_playwright(keyword: str, marketplace: str, page: int, session_id: str) -> str | None:
"""Descarga una página de Amazon usando Playwright con proxy residencial."""
mp = MARKETPLACES[marketplace]
proxy_username = f"{PROXYHAT_USER}-country-{mp['country']}-session-{session_id}"
url = f"{mp['base_url']}/s?k={keyword}&page={page}"
with sync_playwright() as p:
browser = p.chromium.launch(
headless=True,
proxy={
"server": f"http://{PROXYHAT_GATEWAY}:{PROXYHAT_PORT}",
"username": proxy_username,
"password": PROXYHAT_PASS,
},
)
context = browser.new_context(
user_agent=USER_AGENTS[0],
locale=mp["lang"],
viewport={"width": 1920, "height": 1080},
)
page_obj = context.new_page()
try:
page_obj.goto(url, timeout=30000, wait_until="domcontentloaded")
page_obj.wait_for_timeout(2000) # Esperar render JS
html = page_obj.content()
return html
except Exception as e:
logger.error(f"Playwright error: {e}")
return None
finally:
browser.close()
Consejos de producción
Scheduling diario
Programa el tracker para que se ejecute una vez al día por ASIN y keyword. Amazon actualiza rankings con frecuencia, pero un intervalo de 24 horas es suficiente para detectar tendencias. Usa cron en Linux o APScheduler en Python.
from apscheduler.schedulers.blocking import BlockingScheduler
def daily_track():
conn = init_db()
for kw in KEYWORDS:
result = track_keyword(kw, TARGET_ASIN, "com", max_pages=5)
save_ranking(conn, result)
time.sleep(3)
conn.close()
scheduler = BlockingScheduler()
scheduler.add_job(daily_track, "cron", hour=6, minute=30)
scheduler.start()
Reintentos con backoff exponencial
import random
def fetch_with_retry(keyword: str, marketplace: str, page: int, session_id: str, max_retries: int = 3) -> str | None:
"""Fetch con reintentos y backoff exponencial."""
for attempt in range(max_retries):
html = fetch_amazon_page(keyword, marketplace, page, session_id)
if html is not None and not detect_captcha(html):
return html
wait = (2 ** attempt) + random.uniform(0, 1)
logger.warning(f"Reintento {attempt + 1}/{max_retries} en {wait:.1f}s")
time.sleep(wait)
return None
Detección de CAPTCHA y indexación
Amazon muestra CAPTCHAs cuando detecta comportamiento automatizado. Si recibes una página con CAPTCHA:
- Detén inmediatamente las peticiones para esa sesión.
- Cambia el session ID del proxy para obtener una nueva IP.
- Espera 60-120 segundos antes de reintentar.
- Si persiste, reduce la frecuencia global de peticiones.
Además, comprueba siempre si el ASIN está indexado. Si un ASIN no aparece en las primeras 5 páginas para ninguna keyword, puede significar que está desindexado o que Amazon lo ha penalizado. Esto es tan importante como la posición misma.
Métricas a monitorizar
- Success rate: Porcentaje de peticiones que devuelven HTML válido (objetivo: >95%).
- Latencia media: Tiempo de respuesta del proxy + Amazon (objetivo: <3000 ms).
- CAPTCHA rate: Porcentaje de páginas con CAPTCHA (objetivo: <2%).
- Concurrencia: Máximo de peticiones simultáneas (recomendado: 5-10 concurrentes).
Consideraciones éticas y legales
El scraping de Amazon ocupa un espacio legal complejo. Algunas recomendaciones:
- Rastrea solo listados propios o datos públicos. Evita extraer datos de competidores a escala masiva.
- Respeta robots.txt. Amazon permite ciertas rutas pero restringe otras.
- Throttle agresivo. 1-3 segundos entre peticiones es lo mínimo aceptable.
- Considera la SP-API. Para vendedores registrados, la SP-API de Amazon ofrece acceso oficial a datos de catálogo, pedidos e informes. Es la opción más segura y sostenible.
- Cumple con GDPR/CCPA. No almacenes datos personales de vendedores o reviewers.
Para más información sobre proxies y web scraping, consulta nuestra guía de web scraping con proxies y nuestra guía de SERP tracking.
Configuración de ProxyHat y enlaces internos
Para configurar tus proxies residenciales en ProxyHat:
- Accede a tu panel en dashboard.proxyhat.com.
- Consulta las documentación oficial de ProxyHat para detalles de autenticación.
- Revisa las ubicaciones disponibles para geo-targeting.
- Compara los planes y precios según tu volumen de peticiones.
Recuerda: para amazon.com usa -country-US, para amazon.de usa -country-DE, y siempre usa -session-{id} para mantener la paginación estable entre páginas.
Puntos clave (Key Takeaways)
- Amazon es un SERP distinto a Google: el ranking depende de relevancia comercial y sales velocity, no solo de SEO on-page.
- Los proxies residenciales con geo-targeting por país son obligatorios para obtener resultados correctos por marketplace.
- Usa sesiones sticky (
-session-{id}) para mantener la paginación estable entre páginas 1-5.- Separa siempre resultados orgánicos de Sponsored: la posición orgánica real es la métrica que importa.
- Detecta CAPTCHas temprano y cambia de IP inmediatamente para no quemar la sesión.
- Comprueba si el ASIN está indexado: no aparecer en 5 páginas puede indicar penalización.
- Considera la SP-API de Amazon como alternativa oficial cuando sea accesible.
- Throttle de 1-3 segundos entre peticiones y 3 segundos entre keywords es el mínimo ético.
Preguntas frecuentes
¿Qué es el seguimiento de rankings de palabras clave en Amazon con proxies?
Es la práctica de consultar las páginas de resultados de búsqueda de Amazon a través de proxies residenciales para registrar la posición orgánica de un ASIN para una keyword concreta y un marketplace específico. Los proxies evitan bloqueos por IP y permiten simular ubicaciones geográficas reales, asegurando que los resultados correspondan al marketplace correcto.
¿Por qué importa el seguimiento de rankings de Amazon con proxies para usuarios de proxies?
Amazon aplica sistemas anti-bot agresivos y localiza resultados por marketplace. Sin proxies residenciales con geo-targeting por país, las peticiones se bloquean o devuelven resultados irrelevantes. Los proxies permiten mantener sesiones estables para paginación, rotar IPs para evitar detección y obtener posiciones orgánicas reales por marketplace, lo que es crítico para vendedores que operan en múltiples países.
¿Qué tipo de proxy funciona mejor para el seguimiento de rankings de Amazon?
Los proxies residenciales son los más efectivos porque usan IPs de hogares reales y son mucho menos detectables por Amazon. Los proxies de datacenter suelen ser bloqueados en minutos. Para Amazon se recomienda geo-targeting por país (US para amazon.com, DE para amazon.de, GB para amazon.co.uk) y sesiones sticky con -session-{id} para mantener la paginación estable entre páginas.
¿Cómo se evitan los bloqueos al implementar seguimiento de rankings de Amazon con proxies?
Usa proxies residenciales con rotación, sesiones sticky para paginación, throttling de 1-3 segundos entre peticiones, rotación de User-Agent, detección temprana de CAPTCHAs, reintentos con backoff exponencial y respeto a robots.txt. Si detectas un CAPTCHA, detén las peticiones, cambia el session ID para obtener una nueva IP y espera 60-120 segundos. Considera también la SP-API de Amazon para acceso oficial a datos cuando esté disponible.
¿Es legal hacer scraping de Amazon para tracking de keywords?
El scraping de datos públicos de Amazon ocupa un espacio legal complejo. En EE.UU., la CFAA puede aplicar al acceso no autorizado. En la UE, el GDPR regula datos personales. Recomendamos rastrear solo listados propios o datos públicos, respetar robots.txt y los Términos de Servicio de Amazon, y considerar la SP-API como alternativa oficial. Esta guía es educativa y no constituye asesoría legal.






