Files
chemavx-seo-tools/MANUAL-GSC.md
T
ChemaVXandClaude Opus 5 896d723f15 seo_gsc: el marcador — datos reales de Search Console
Toda la caja de herramientas miraba hacia dentro (enlaces, sitemap, reglas de
metas): decía si el sitio está bien construido, no si alguien lo encuentra. Los
únicos datos de resultado en la máquina eran una exportación manual del
2026-06-17, anterior al remate de metas del 23 de junio — o sea, cuarenta días
optimizando sin saber el resultado.

Cuenta de servicio en vez de OAuth, porque aquí no hay navegador (misma piedra
que con Gemini). Sin librerías de Google: el JWT se firma con PyJWT y se canjea
a mano, treinta líneas en lugar de media docena de dependencias.

Lo que de verdad aporta es `informe`: la lista de páginas que POSICIONAN Y NO
SE PINCHAN, con los clics que faltan si el CTR fuese el normal de su posición.
Y `antes-despues`, escrito para responder de una vez si el remate del 23-jun
sirvió: la API guarda 16 meses, así que el histórico está disponible desde el
primer minuto y no hay que esperar semanas para comparar.

Probado lo que se puede probar sin credencial: firma RS256 emitida y verificada
con su pública, los dos caminos de fallo de la credencial (ausente y con
permisos flojos), y la lógica de análisis contra datos sintéticos calcados del
caso Nimitz real (pos 6,44 / 2.452 impresiones / 0,16% CTR), que sale marcada
con +111 clics de hueco mientras la página sana no se marca.

El timer queda instalado y PARADO: sin credencial fallaría cada lunes, y un
timer que falla siempre enseña a ignorar los avisos.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 20:36:45 +00:00

4.9 KiB

El marcador — Search Console (seo_gsc.py)

Lo que faltaba en toda la caja de herramientas: datos de resultado. El resto mira hacia dentro (enlaces, sitemap, reglas de metas); esto mira lo único que decide si el trabajo sirvió — cuánta gente nos encuentra y cuánta pincha.

Es de solo lectura. No escribe en los blogs ni en Search Console.

Puesta en marcha (cuatro pasos de navegador, una sola vez)

La API es gratis. Hace falta una cuenta de servicio, no OAuth: en esta máquina no hay navegador y el flujo interactivo no funciona headless — la misma piedra con la que ya tropezamos en Gemini.

  1. Proyecto y API. En https://console.cloud.google.com crea un proyecto (o reutiliza uno) y activa Google Search Console API en APIs y servicios → Biblioteca.

  2. Cuenta de servicio. IAM y administración → Cuentas de servicio → Crear. No necesita ningún rol de IAM: el permiso que importa se da en el paso 4. Luego Claves → Añadir clave → Crear nueva → JSON y descarga el fichero.

  3. La clave, en su sitio. En esta máquina:

    mkdir -p ~/.config/gsc
    mv ~/Descargas/EL-FICHERO.json ~/.config/gsc/service-account.json
    chmod 600 ~/.config/gsc/service-account.json
    

    La herramienta se niega a arrancar si el fichero es legible por otros. Está fuera de cualquier repo; no la metas en git.

  4. Permiso en Search Console. Este es el paso que se olvida. Para cada una de las dos propiedades (theexclusionzone.com y zonadeexclusion.com): Configuración → Usuarios y permisos → Añadir usuario → pega el client_email de la credencial (algo como nombre@proyecto.iam.gserviceaccount.com) → permiso Restringido, que basta para leer.

    ⚠️ Si zonadeexclusion.com no está dada de alta en Search Console, hay que verificarla antes. Del blog ES no había ni una sola exportación, así que es probable que ni exista la propiedad.

  5. Arrancar el vigilante. El timer se instala parado a propósito: sin credencial fallaría cada lunes, y un timer que falla siempre enseña a ignorar los avisos. Se enciende cuando el canario pasa:

    seo-gsc comprueba && sudo systemctl enable --now seo-gsc-watch.timer
    

Comprobación:

seo-gsc sitios        # tienen que salir las dos propiedades
seo-gsc comprueba     # canario: recorre el camino real eslabón por eslabón

Si el nombre que devuelve sitios no coincide con el configurado (puede ser sc-domain:x.com o https://www.x.com/, con barra final), corrige el diccionario SITIOS en seo_gsc.py con el nombre exacto.

Uso diario

seo-gsc pull          # descarga y acumula en SQLite (idempotente)
seo-gsc               # sin argumentos = informe de 28 días
seo-gsc informe --dias 7

La primera descarga se trae 16 meses, que es todo lo que Google guarda. Eso significa que el histórico está disponible desde el minuto uno: no hay que esperar semanas para tener con qué comparar.

La pregunta pendiente

El remate de metas fue el 23 de junio y los únicos datos que había eran del 17. En cuanto haya credencial:

seo-gsc antes-despues --corte 2026-06-23 --dias 28

Qué mira el informe

  • Movimiento de clics, impresiones y CTR contra el periodo anterior.
  • Páginas que posicionan y no se pinchan. Es la lista que importa: si algo está en posición 6 con 2.400 impresiones y 0,16% de CTR, no tiene un problema de posicionamiento, tiene un problema de titular. Cada línea estima los clics que faltan si el CTR fuese el normal de esa posición.
  • Consultas con impresiones y cero clics, que son temas donde ya salimos y no convencemos.

El CTR "normal" sale de una curva de referencia del sector, no de una medición nuestra: sirve para ordenar candidatas, no para prometer tráfico. Solo se avisa por encima de 150 impresiones, porque por debajo el CTR es ruido.

Automático

seo-gsc-watch.timer — lunes 06:15 UTC: descarga y manda el informe por Telegram, pero solo si hay páginas desperdiciadas y el informe ha cambiado desde el último aviso. Misma disciplina que seo-watch: si no hay nada que decir, se calla.

systemctl status seo-gsc-watch.timer
journalctl -u seo-gsc-watch -n 40

Notas de implementación

  • Sin librerías de Google. El JWT se firma con PyJWT y se canjea a mano. Son treinta líneas y evita arrastrar media docena de dependencias para tres peticiones.
  • Los datos tardan 2-3 días en consolidarse. Todo termina 3 días atrás y se pide dataState=final; pedir "ayer" devuelve cifras que luego cambian solas.
  • La posición se pondera por impresiones. Promediar posiciones a pelo da el mismo peso a un día de 2 impresiones que a uno de 500.
  • La API pagina de 25.000 en 25.000 y no avisa de que quedan más filas.
  • El almacén es SQLite en ~/.local/state/seo-gsc/gsc.db; pull es idempotente y resolapa un día por si el último se consolidó después.