Anal铆tica & Datos

#01 C贸mo uso Python para detectar oportunidades de keywords que GSC no te muestra directamente

PythonSEOGoogle Search ConsoleData ScienceKeywords

C贸mo uso Python para detectar oportunidades de keywords que GSC no te muestra directamente

Si trabajas en SEO y usas Google Search Console a diario, ya sabes lo que te voy a decir: la interfaz te da lo justo. Puedes ver las queries que generan impresiones, los clics, el CTR y la posici贸n media. Bien. Pero ah铆 se acaba la fiesta.

El problema no es que GSC sea mala herramienta, sino que te muestra los datos de forma agregada y sin contexto. Y cuando tienes un sitio con cientos o miles de URLs, navegar por esa interfaz para encontrar oportunidades reales es como buscar una aguja en un pajar con los ojos cerrados.

Hace tiempo que resolv铆 esto con Python. Te cuento exactamente c贸mo lo hago.

Por qu茅 la interfaz de GSC se queda corta

Antes de entrar en c贸digo, quiero que entiendas bien el problema. GSC te muestra hasta 1.000 queries en su interfaz est谩ndar. Si tienes m谩s volumen, ya vas perdiendo datos. Adem谩s, no puedes cruzar f谩cilmente queries con URLs, ni filtrar por rangos de posici贸n y CTR al mismo tiempo, ni detectar patrones entre keywords que comparten intenci贸n.

Lo que necesitas para encontrar oportunidades reales es poder hacerte preguntas como estas:

  • 驴Qu茅 keywords tienen muchas impresiones pero CTR muy bajo? (posible problema de t铆tulo o metadescripci贸n)
  • 驴Qu茅 queries est谩n en posici贸n 8-15 y podr铆an subir con un peque帽o empuj贸n?
  • 驴Hay URLs que reciben tr谩fico por keywords que no est谩n optimizadas en la p谩gina?

Para responder a esto de forma sistem谩tica, necesitas los datos en crudo y una herramienta que te deje manipularlos. Ah铆 entra Python.

El proceso paso a paso

1. Exportar los datos de GSC

Lo primero es sacar los datos fuera de la interfaz. Tienes dos opciones: exportar el CSV manualmente desde la pesta帽a de Rendimiento, o usar la Google Search Console API para automatizarlo. Yo uso la API cuando trabajo con proyectos recurrentes, pero para empezar el CSV es m谩s que suficiente.

Exporta con el filtro de fecha m谩s amplio posible (los 煤ltimos 16 meses si puedes) y aseg煤rate de activar la vista de Query + P谩gina para tener el cruce entre ambas dimensiones.

2. Cargar y limpiar los datos con pandas

Una vez tienes el CSV, esto es lo primero que ejecuto:

import pandas as pd

df = pd.read_csv("gsc_export.csv")

# Renombrar columnas si vienen en ingl茅s
df.columns = ["query", "url", "clicks", "impressions", "ctr", "position"]

# Limpiar el CTR (viene como string tipo "3.45%")
df["ctr"] = df["ctr"].str.replace("%", "").astype(float) / 100

# Eliminar filas sin datos
df = df.dropna()

print(df.shape)
print(df.head())

Nada del otro mundo, pero es importante hacer esta limpieza antes de cualquier an谩lisis. El CTR en formato porcentaje como string es uno de esos errores que te puede dar dolores de cabeza si no lo corriges desde el principio.

3. Detectar oportunidades por posici贸n y CTR

Aqu铆 empieza lo interesante. El segmento que m谩s me gusta analizar es el de keywords en posici贸n 6-15 con volumen de impresiones razonable. Son las que est谩n cerca del top pero no terminan de entrar, y suelen responder bien a mejoras de contenido o de SEO on-page.

oportunidades = df[
    (df["position"] >= 6) &
    (df["position"] <= 15) &
    (df["impressions"] >= 100)
].copy()

oportunidades = oportunidades.sort_values("impressions", ascending=False)

print(oportunidades[["query", "url", "impressions", "ctr", "position"]].head(20))

Con este filtro obtienes un listado de queries que ya tienen visibilidad, pero que todav铆a no est谩n convirtiendo bien. Son candidatos perfectos para una optimizaci贸n r谩pida.

4. Cruzar CTR real con CTR esperado por posici贸n

Este es el an谩lisis que m谩s me gusta y el que m谩s valor aporta. La idea es comparar el CTR que est谩 obteniendo cada keyword con el CTR esperado para esa posici贸n, seg煤n benchmarks conocidos del sector.

# CTR medio esperado por posici贸n (valores aproximados seg煤n estudios del sector)
ctr_esperado = {
    1: 0.28, 2: 0.15, 3: 0.11, 4: 0.08, 5: 0.06,
    6: 0.04, 7: 0.03, 8: 0.025, 9: 0.022, 10: 0.02
}

df["position_round"] = df["position"].round().astype(int)
df["ctr_esperado"] = df["position_round"].map(ctr_esperado)

df["ctr_gap"] = df["ctr_esperado"] - df["ctr"]

# Keywords con CTR muy por debajo del esperado
bajo_ctr = df[
    (df["position_round"] <= 10) &
    (df["ctr_gap"] > 0.02)
].sort_values("ctr_gap", ascending=False)

print(bajo_ctr[["query", "url", "position", "ctr", "ctr_esperado", "ctr_gap"]].head(20))

Ojo: los valores de CTR esperado var铆an mucho seg煤n el sector, el tipo de query y si hay features de SERP (featured snippets, PAA, etc.). 脷salos como referencia, no como verdad absoluta. Lo importante es el patr贸n, no el n煤mero exacto.

Cuando encuentras una keyword con posici贸n 3 y CTR del 2% cuando lo esperable ser铆a un 11%, tienes una se帽al clara de que algo falla: el t铆tulo no es atractivo, la metadescripci贸n no engancha, o hay un snippet que est谩 robando los clics.

5. Exportar el resultado para trabajar con 茅l

Al final del proceso, exporto todo a un CSV limpio para revisarlo con calma o compartirlo con el equipo:

bajo_ctr.to_csv("oportunidades_ctr.csv", index=False)
oportunidades.to_csv("oportunidades_posicion.csv", index=False)

Lo que hago con estos datos despu茅s

Los resultados de este an谩lisis me sirven para dos cosas principalmente:

  • Priorizar p谩ginas para optimizaci贸n on-page: si una URL aparece varias veces con diferentes queries con CTR bajo, es candidata clara a revisar t铆tulo, H1 y metadescripci贸n.
  • Identificar contenido que necesita expansi贸n: si una p谩gina posiciona en top 10 por una query que no est谩 bien desarrollada en el texto, a帽adir contenido relevante suele mover la aguja.

No es un proceso m谩gico, pero s铆 es sistem谩tico. Y eso, en SEO, ya es mucho.


Si tienes cualquier duda sobre c贸mo adaptar esto a tu caso concreto, escr铆beme. Y si quieres ver c贸mo automatizo este proceso completo con la API de GSC para no tener que exportar CSVs manualmente, eso lo cuento en otro post.