seo-watch: vigila la indexación en Google (ventana 5-60 días, huella sin la antigüedad)

This commit is contained in:
ChemaVX
2026-08-31 18:07:39 +00:00
parent 1a9012db56
commit 1f9be7ed2a
3 changed files with 300 additions and 19 deletions
+58 -14
View File
@@ -599,6 +599,59 @@ def cmd_antes_despues(a):
# ──────────────────────────────── utilidades ────────────────────────────────
def url_canonica(site, slug_o_url):
"""La URL tal y como Google la tiene. Ojo: EN canoniza en **www** y ES en el
APEX, al revés — declararlo en un solo sitio evita inspeccionar una URL que
la propiedad ni siquiera ve."""
if slug_o_url.startswith("http"):
return slug_o_url
base = ("https://www.theexclusionzone.com/" if site == "en"
else "https://zonadeexclusion.com/")
return f"{base}{slug_o_url.strip('/')}/"
def inspecciona_url(tok, site, slug_o_url):
"""Estado de indexación de UNA url según Google.
Devuelve el `indexStatusResult` con la `url` consultada añadida, o
`{"error": "<código>"}` si la API no contesta 200. Existe como función y no
solo como comando porque la usan dos consumidores —este CLI y el vigilante
diario— y el endpoint, la propiedad y el host canónico deben declararse una
vez, no dos.
"""
import requests
url = url_canonica(site, slug_o_url)
r = requests.post(
"https://searchconsole.googleapis.com/v1/urlInspection/index:inspect",
headers={"Authorization": f"Bearer {tok}"}, timeout=60,
json={"inspectionUrl": url, "siteUrl": SITIOS[site]["propiedad"]})
if r.status_code != 200:
return {"error": str(r.status_code), "detalle": r.text[:200], "url": url}
d = dict(r.json()["inspectionResult"]["indexStatusResult"])
d["url"] = url
return d
def etiqueta_indexacion(d):
"""(icono, texto corto) de un `indexStatusResult`.
Los tres estados de «no indexado» NO son el mismo problema y por eso no se
colapsan: «no lo conoce» es que nunca llegó, «descubierto» es que está en
cola, y «rastreado sin indexar» es que lo leyó y decidió no quedárselo —
solo el último apunta al contenido.
"""
if d.get("error"):
return "", f"error {d['error']}"
cob = d.get("coverageState", "?")
if d.get("verdict") == "PASS":
return "", "indexado"
if cob.startswith("Crawled"):
return "🟡", "rastreado, sin indexar"
if cob.startswith("Discovered"):
return "🔵", "descubierto, sin rastrear"
return "", "Google no lo conoce"
def cmd_inspecciona(a):
"""¿Conoce Google esta URL? Veredicto suyo, no deducción nuestra.
@@ -607,31 +660,22 @@ def cmd_inspecciona(a):
unknown to Google»: nunca rastreados. Sin esto se habría confundido con un
problema de contenido y se habrían reescrito titulares para nada.
"""
import requests
tok = token(carga_credencial())
prop = SITIOS[a.site]["propiedad"]
base = ("https://www.theexclusionzone.com/" if a.site == "en"
else "https://zonadeexclusion.com/")
malas = 0
for slug in a.slugs:
url = slug if slug.startswith("http") else f"{base}{slug.strip('/')}/"
r = requests.post(
"https://searchconsole.googleapis.com/v1/urlInspection/index:inspect",
headers={"Authorization": f"Bearer {tok}"}, timeout=60,
json={"inspectionUrl": url, "siteUrl": prop})
if r.status_code != 200:
print(f"{r.status_code} {url}\n {r.text[:200]}")
i = inspecciona_url(tok, a.site, slug)
if i.get("error"):
print(f"{i['error']} {i['url']}\n {i.get('detalle','')}")
malas += 1
continue
i = r.json()["inspectionResult"]["indexStatusResult"]
ok = i.get("verdict") == "PASS"
malas += 0 if ok else 1
print(f" {'' if ok else ''} {i.get('coverageState', '?')}")
print(f" rastreo {i.get('lastCrawlTime', '')[:10]} "
f"robots {i.get('robotsTxtState', '?')}")
if i.get("googleCanonical") and i["googleCanonical"] != url:
if i.get("googleCanonical") and i["googleCanonical"] != i["url"]:
print(f" ⚠ Google canoniza a: {i['googleCanonical']}")
print(f" {url}")
print(f" {i['url']}")
return 1 if malas else 0