validador: un par consolidado no es una colisión, es su solución
topic_collision marcaba como colisión los dos artículos del acto de Grusch del 9 de junio, cuando el de mayo lleva canonical_url apuntando al otro desde hace semanas. Google ya sabe cuál manda y Ghost excluye al secundario del sitemap: no hay nada que arreglar, y el propio mensaje ofrecía «interlink deliberately» como salida, que es justo lo que había. Un aviso que no se puede resolver nunca es peor que no avisar, porque enseña a ignorar al validador entero — la misma razón por la que esta mañana retiramos la comprobación de reddit-intel de backup-verify. El par se salta cuando cualquiera de los dos declara canónico al otro. Si el canónico apunta a un tercero, la colisión sigue avisando: comprobado con los cuatro casos (sin canónico dispara, en las dos direcciones se calla, canónico a un tercero dispara). fetch_corpus pide ya canonical_url, que antes no traía.
This commit is contained in:
@@ -270,6 +270,19 @@ def _sig_tokens(title, slug):
|
|||||||
if len(t) >= 3 and t not in TOPIC_STOPWORDS and not _YEAR_RE.fullmatch(t)}
|
if len(t) >= 3 and t not in TOPIC_STOPWORDS and not _YEAR_RE.fullmatch(t)}
|
||||||
|
|
||||||
|
|
||||||
|
def _canoniza_a(post):
|
||||||
|
"""Slug al que este post declara canónico, o None si no declara ninguno.
|
||||||
|
|
||||||
|
Ghost guarda una URL completa; aquí solo interesa el último segmento, que es
|
||||||
|
lo único comparable con el slug de otro post del mismo sitio.
|
||||||
|
"""
|
||||||
|
url = _s(post.get("canonical_url"))
|
||||||
|
if not url:
|
||||||
|
return None
|
||||||
|
resto = url.split("?")[0].split("#")[0].rstrip("/")
|
||||||
|
return resto.rsplit("/", 1)[-1] or None
|
||||||
|
|
||||||
|
|
||||||
def _hook(title):
|
def _hook(title):
|
||||||
return _s(title).split(":")[0].strip().lower()
|
return _s(title).split(":")[0].strip().lower()
|
||||||
|
|
||||||
@@ -290,10 +303,19 @@ def topic_collision(post, corpus):
|
|||||||
c_hook = _hook(post.get("title"))
|
c_hook = _hook(post.get("title"))
|
||||||
c_slug_toks = _tokens(_s(post.get("slug")).replace("-", " "))
|
c_slug_toks = _tokens(_s(post.get("slug")).replace("-", " "))
|
||||||
|
|
||||||
|
c_canon = _canoniza_a(post)
|
||||||
|
|
||||||
for other in corpus:
|
for other in corpus:
|
||||||
if other.get("id") == post.get("id"):
|
if other.get("id") == post.get("id"):
|
||||||
continue
|
continue
|
||||||
o_title, o_slug = _s(other.get("title")), _s(other.get("slug"))
|
o_title, o_slug = _s(other.get("title")), _s(other.get("slug"))
|
||||||
|
# Un par consolidado NO es una colisión: es la solución a una colisión.
|
||||||
|
# Cuando uno de los dos declara al otro como canónico, Google ya sabe
|
||||||
|
# cuál manda y Ghost excluye al secundario del sitemap. Marcarlo sería
|
||||||
|
# pedir que se arregle algo que está arreglado — y el aviso, al no poder
|
||||||
|
# resolverse nunca, enseña a ignorar al validador.
|
||||||
|
if c_canon == o_slug or _canoniza_a(other) == _s(post.get("slug")):
|
||||||
|
continue
|
||||||
o_years = _case_years(o_title, o_slug)
|
o_years = _case_years(o_title, o_slug)
|
||||||
o_sig = _sig_tokens(o_title, o_slug)
|
o_sig = _sig_tokens(o_title, o_slug)
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -50,8 +50,10 @@ def fetch_corpus():
|
|||||||
fd, path = tempfile.mkstemp(suffix=".json", prefix="seo_corpus_")
|
fd, path = tempfile.mkstemp(suffix=".json", prefix="seo_corpus_")
|
||||||
os.close(fd)
|
os.close(fd)
|
||||||
try:
|
try:
|
||||||
|
# canonical_url hace falta para NO marcar como colisión un par que ya
|
||||||
|
# está consolidado a propósito (ver topic_collision en seo_rules.py).
|
||||||
cmd = (f"ghst-en --json post list --limit all "
|
cmd = (f"ghst-en --json post list --limit all "
|
||||||
f"--fields id,title,slug,status > {path}")
|
f"--fields id,title,slug,status,canonical_url > {path}")
|
||||||
r = subprocess.run(cmd, shell=True, stderr=subprocess.PIPE, text=True)
|
r = subprocess.run(cmd, shell=True, stderr=subprocess.PIPE, text=True)
|
||||||
if r.returncode != 0:
|
if r.returncode != 0:
|
||||||
print(f" [warn] corpus fetch failed — topic collision NOT checked:\n{r.stderr}",
|
print(f" [warn] corpus fetch failed — topic collision NOT checked:\n{r.stderr}",
|
||||||
|
|||||||
Reference in New Issue
Block a user