seo: quién enlaza HACIA un post, que es lo que decide si Google lo encuentra

Toda la caja miraba los enlaces en un solo sentido — a quién enlazo yo — y
esa es la pregunta del lector, no la de Google. Dos posts de EN publicados
el 21-jul seguían siendo «URL is unknown to Google» una semana después:
tenían enlaces entrantes, pero solo desde artículos con 8 y 12 impresiones
de por vida, páginas que Google casi no visita.

seo_semantic gana dos subcomandos: `padrinos <slug>`, que ordena candidatos
por tráfico REAL del origen (Search Console) en vez de por similitud, y
`descubrimiento`, que lista los publicados a los que no llega ni un enlace
desde el tercio fuerte del sitio. Salen 21 de 32 en EN y 21 de 31 en ES.
Ambos descartan los posts con canonical_url propio: Ghost los excluye del
sitemap por consolidados, así que apadrinar desde ahí no sirve de nada — el
de Grusch de mayo tiene 1.728 impresiones y habría encabezado la lista.

seo_gsc gana `inspecciona`, que pregunta el veredicto a Google en vez de
deducirlo. Es lo que separa «posiciona mal» de «no lo ha visto nunca», que
son problemas opuestos y se arreglan al revés.

Y el checklist del remate incorpora el paso 3b: el informe tiene que traer
el comando de enlace entrante listo para copiar. No lo aplica el agente
porque eso es editar otro post publicado, y eso sigue prohibido.

⚠️ Anotado en el docstring: contar enlaces internos desde el HTML público da
«cero huérfanos», porque el tema mete navegación y feed de relacionados que
parecen enlaces del cuerpo. La verdad está en el html del CLI de Ghost.
This commit is contained in:
ChemaVX
2026-07-28 06:55:24 +00:00
parent 4609a21f21
commit f7be9ccb3e
3 changed files with 261 additions and 1 deletions
+46
View File
@@ -41,6 +41,11 @@ Subcomandos:
Compara la misma ventana antes y después de una fecha. Escrito para
responder de una vez si el remate de metas del 23-jun sirvió de algo.
seo_gsc.py inspecciona <slug…> --site en|es
El veredicto de Google sobre una URL: indexada, rastreada cuándo, y a
qué canoniza. Distingue «posiciona mal» de «no lo ha visto nunca», que
son problemas opuestos. Sale con código 1 si alguna no está indexada.
seo_gsc.py comprueba
Canario: recorre el camino REAL (credencial → firma → token → API) y
dice en qué eslabón exacto se rompe. No simula nada.
@@ -449,6 +454,42 @@ def cmd_antes_despues(a):
# ──────────────────────────────── utilidades ────────────────────────────────
def cmd_inspecciona(a):
"""¿Conoce Google esta URL? Veredicto suyo, no deducción nuestra.
Es la única forma de distinguir «posiciona mal» de «no lo ha visto». El
2026-07-28 dos posts de EN publicados una semana antes salieron con «URL is
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]}")
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:
print(f" ⚠ Google canoniza a: {i['googleCanonical']}")
print(f" {url}")
return 1 if malas else 0
def cmd_sitios(a):
sa = carga_credencial()
print(f"cuenta de servicio: {sa['client_email']}")
@@ -555,6 +596,11 @@ def main():
p.add_argument("--site", choices=("en", "es", "todos"), default="todos")
p.set_defaults(func=cmd_antes_despues)
p = sub.add_parser("inspecciona", help="¿conoce Google estas URLs?")
p.add_argument("slugs", nargs="+", help="slugs o URLs completas")
p.add_argument("--site", choices=("en", "es"), required=True)
p.set_defaults(func=cmd_inspecciona)
p = sub.add_parser("comprueba", help="canario del camino real")
p.set_defaults(func=cmd_comprueba)