Estreno con credencial. Dos cosas que solo se ven con la API delante: La propiedad ES no es de dominio sino de prefijo de URL (https://zonadeexclusion.com/), así que sc-domain:zonadeexclusion.com no existía y el canario lo cazó en el paso 4. Queda anotado que ese tipo de propiedad no ve www ni http. Y el histórico no es de 16 meses: las dos propiedades se dieron de alta en mayo de 2026 y el dato más antiguo es del 2026-05-09. El manual prometía comparar contra el año pasado y eso no va a existir nunca.
132 lines
5.6 KiB
Markdown
132 lines
5.6 KiB
Markdown
# 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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
seo-gsc comprueba && sudo systemctl enable --now seo-gsc-watch.timer
|
|
```
|
|
|
|
Comprobación:
|
|
|
|
```bash
|
|
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.
|
|
|
|
Es lo que pasó al estrenarlo: las dos propiedades **no son del mismo tipo**. EN
|
|
es de dominio y ES es de prefijo de URL (`https://zonadeexclusion.com/`), que
|
|
solo ve lo que cuelga de ese prefijo exacto — ni `www`, ni `http`. Sirve porque
|
|
el canónico del ES es el apex, pero si algún día se publica bajo `www` no
|
|
aparecería aquí. Darla de alta como propiedad de dominio lo arreglaría de raíz.
|
|
|
|
## Uso diario
|
|
|
|
```bash
|
|
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 pide **16 meses**, que es todo lo que Google guarda, pero
|
|
lo que llega es lo que haya. Medido el 2026-07-28: el dato más antiguo es del
|
|
**2026-05-09** en las dos propiedades, porque se dieron de alta entonces. Google
|
|
no guarda nada de antes de que exista la propiedad, así que **no hay histórico
|
|
anterior a mayo de 2026 y nunca lo habrá**. Cuidado con leer «16 meses» y creer
|
|
que se puede comparar contra el año pasado.
|
|
|
|
### 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:
|
|
|
|
```bash
|
|
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.
|
|
|
|
```bash
|
|
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.
|