Automatizar el seguimiento de rankings parece un cron job envuelto en una llamada a una API, y durante la primera semana lo es. Los problemas llegan después: dos ejecuciones que no son comparables porque una salió a una hora distinta, un día que falta en la serie y que nadie nota hasta que un gráfico se ve mal, una alerta que salta por ruido, y un cliente preguntando por qué tu número no coincide con el suyo.
Ninguno de esos son problemas de captura de datos. Son problemas de pipeline, y merece la pena diseñarlos antes de tener seis meses de datos con agujeros. Aquí está la construcción.
Definir primero el contrato de medición
Antes de escribir cualquier código, fija las variables que hacen que dos mediciones sean comparables, porque un ranking no significa nada sin ellas: la ubicación, el dispositivo, el idioma y si la búsqueda está personalizada. Esas decisiones se convierten en constantes de tu job, no en opciones por ejecución.
La razón es que cualquier deriva en ellas aparece en tus datos como un movimiento de ranking que nunca ocurrió. Una palabra clave medida desde una ciudad el lunes y desde otra el martes parecerá haberse movido. El razonamiento completo está en measuring accurate keyword rankings, y la versión corta es que la consistencia importa más que la precisión absoluta.
Escribe el contrato como un esquema, porque también es lo que entregarás a un cliente cuando pregunte qué significan tus números:
# one row per keyword per market: this is the unit of measurement
TARGET = {
"keyword": "residential proxies",
"country": "de",
"city": "berlin", # optional, only where local intent matters
"language": "de",
"device": "desktop",
}
Capturar datos mediante una API de SERP
Puedes construir tú mismo la capa de recogida sobre proxies, o llamar a una API que devuelva resultados ya parseados. Para un job de monitorización diaria, la vía de la API elimina las dos cargas de mantenimiento que realmente consumen tiempo: mantenerse al día con los cambios de maquetación de la página de resultados, y ejecutar la infraestructura de recogida.
Una SERP API toma los parámetros anteriores y devuelve resultados estructurados, de modo que tu job es una petición y una escritura en lugar de una captura y un parser:
import requests, datetime as dt
def fetch_serp(t):
r = requests.get("https://serp.shifter.io/v1", timeout=45, params={
"api_key": API_KEY,
"q": t["keyword"],
"gl": t["country"], # market
"hl": t["language"], # interface language
"location": t.get("city"),
"device": t["device"],
})
r.raise_for_status()
return r.json()
Comprueba los nombres de parámetros actuales en la documentación de la API en lugar de confiar en una entrada de blog, ya que estos evolucionan. La alternativa, ejecutar la recogida tú mismo sobre salidas residenciales, se trata en why accurate rank tracking requires residential proxies, y el compromiso es control frente a mantenimiento.
Almacenar el resultado completo, no solo tu posición
El error de diseño más común, y el que resulta caro de deshacer, es almacenar un único número por palabra clave y día.
Almacena la lista completa y ordenada de URLs, la presencia y posición de las funciones del resultado, y el payload en bruto. Tres razones. Los movimientos de tus competidores son el contexto que hace interpretable tu propio movimiento. Los cambios de funciones, como que aparezca un resumen de IA por encima del pliegue, cambian el valor de una posición sin cambiar el número. Y no puedes recoger retroactivamente una SERP del martes pasado, así que todo lo que no almacenaste ha desaparecido.
CREATE TABLE serp_snapshot (
id BIGSERIAL PRIMARY KEY,
keyword TEXT NOT NULL,
country TEXT NOT NULL,
city TEXT,
device TEXT NOT NULL,
captured_at TIMESTAMPTZ NOT NULL,
results JSONB NOT NULL, -- full ranked list
features JSONB NOT NULL, -- ai overview, local pack, shopping
raw JSONB -- keep it, storage is cheaper than regret
);
CREATE INDEX ON serp_snapshot (keyword, country, device, captured_at DESC);
Las posiciones se derivan entonces de las instantáneas en lugar de almacenarse como registro principal, lo que significa que una corrección de parseo puede reaplicarse al histórico en lugar de solo a las ejecuciones futuras.
Programar para la comparabilidad
Ejecuta a la misma hora cada día, dentro de una franja que mantengas estable, porque los resultados varían a lo largo del día y una serie recogida a horas variables tiene una varianza que no puedes atribuir.
Distribuye el trabajo a lo largo de esa franja en lugar de disparar todas las palabras clave a la vez. Una ráfaga es a la vez más dura para la fuente y más propensa a ser limitada, y distribuir el trabajo no te cuesta nada cuando el plazo son horas. Añade jitter entre peticiones, limita la concurrencia, y deja que la ejecución dure una hora en lugar de cuatro minutos.
import random, time
def run_daily(targets, window_seconds=3600):
gap = window_seconds / max(len(targets), 1)
for t in targets:
snapshot = fetch_with_retries(t)
store(t, snapshot)
time.sleep(gap * random.uniform(0.7, 1.3)) # spread, with jitter
Distinguir “sin cambios” de “sin datos”
Este es el detalle que separa una serie fiable de una engañosa. Si una captura falla y no escribes nada, un lector posterior ve un hueco que parece idéntico a un día en el que nada se movió. Entonces una comparación contra “ayer” abarca silenciosamente dos días y reporta un movimiento que no ocurrió.
Registra cada intento con un resultado explícito, y haz que el análisis posterior se niegue a comparar a través de un hueco:
def fetch_with_retries(t, attempts=3):
for i in range(attempts):
try:
data = fetch_serp(t)
if is_valid(data): # sanity-check the payload
return {"status": "ok", "data": data}
record_status(t, "invalid") # parsed, but not a real SERP
except requests.RequestException:
record_status(t, "error")
time.sleep(2 ** i * random.uniform(0.5, 1.5))
return {"status": "failed", "data": None} # written as a failure, not skipped
Validar el payload importa tanto como capturar excepciones, porque una respuesta puede llegar correctamente y aun así no ser una página de resultados utilizable, que es el problema general tratado en detecting blocked or fake content.
Alertar sobre movimientos que signifiquen algo
Una alerta ingenua ante cualquier cambio de posición saltará constantemente, y un sistema de alertas en el que nadie confía es peor que ninguno.
Tres reglas hacen que las alertas sean útiles. Exige un umbral proporcional a la posición, ya que un movimiento de 3 a 6 importa mucho más que de 47 a 50. Exige persistencia, es decir, dos o tres ejecuciones consecutivas, porque los rebotes de un solo día son normales. Y alerta por separado sobre entrar o salir de la primera página, ya que ese límite tiene consecuencias reales de tráfico.
Luego añade la alerta que la gente olvida: la propia salud de la recogida. Si un mercado dejó de devolver datos silenciosamente hace tres días, eso es más urgente que cualquier cambio de ranking, y solo es visible si estás tratando los resultados de las ejecuciones como datos de primera clase, según monitoring a scraping pipeline.
Hacer que los datos se expliquen a sí mismos
Dos añadidos convierten una tabla de rankings en algo que puedes poner delante de un cliente.
Anota tu propia línea temporal: despliegues, publicaciones de contenido, migraciones. La mitad de todas las investigaciones de “por qué hemos bajado” acaban en un lanzamiento que salió esa semana, y un gráfico anotado lo encuentra de inmediato.
Calcula el movimiento a nivel de cartera, no solo posiciones por palabra clave, porque un cambio amplio en muchas palabras clave no relacionadas es un evento distinto al de una página que resbala. Ese es el análisis de volatilidad de detecting SERP volatility and algorithm updates, y solo es posible porque almacenaste el conjunto completo de resultados y no solo tu propia posición.
Recopilar de forma responsable
Cíñete a resultados de búsqueda públicos, respeta los términos de servicio de cada motor, y mantén un ritmo educado en lugar de tratar los límites de velocidad como un obstáculo. Un job de monitorización diaria no tiene ninguna razón para ser agresivo: el plazo es la mañana siguiente, así que distribuir el trabajo no cuesta nada.
La conclusión
El cron job es la parte fácil. Fija primero tu contrato de medición, de modo que la ubicación, el dispositivo, el idioma y la personalización sean constantes y no variables accidentales. Almacena el conjunto completo de resultados y el payload en bruto, no solo tu posición, porque el contexto de los competidores y las funciones del resultado son lo que hace interpretable un número y no puedes volver atrás a por ellos. Ejecuta a una hora consistente, distribuida a lo largo de la franja con jitter. Registra los fallos explícitamente para que un hueco nunca pueda confundirse con estabilidad. Alerta sobre movimientos persistentes y ponderados por posición, además de la salud de la recogida. Luego anota tus propios cambios, para que el gráfico responda a la pregunta que la gente realmente hace.
La capa de captura de datos es una SERP API si quieres resultados parseados sin mantener la recogida, o residential proxies con segmentación por país y ciudad si prefieres ejecutarlo tú mismo, con precios por GB que se adaptan a comprobaciones pequeñas y frecuentes.