seo_rules: el detector de colisiones ya entiende español

El motor se calibró con el corpus inglés y al estrenarlo en zonadeexclusion
daba 6 avisos, 4 de ellos falsos: «que», «los» y «del» pasaban el filtro de
3 caracteres y contaban como identidad de caso. Kenneth Arnold 1947 «colisionaba»
con Roswell 1947 sin compartir más que el año y un artículo determinado.

Dos cambios, y van juntos a propósito:

- Stopwords ES (glue + genéricos del dominio, espejo del bloque inglés).
- _deaccent en el tokenizador: [a-z0-9]+ PARTE en cualquier acento, así que
  "Pentágono" era {pent, gono} y "Fenómenos" era {fen, menos} — basura de 3
  caracteres que ninguna lista de stopwords puede cubrir, y que además nunca
  casaba con el slug de Ghost (que ya viene sin acentos). Sin esto, la mitad
  de las stopwords nuevas no llegarían a aplicarse.

Medido sobre los dos corpus antes de tocar nada: ES 6 → 2 avisos, EN idéntico
(mismos 4 pares: PURSUE 3/4 y el par Grusch). Peaje asumido: «los» deja de
identificar a Los Alamos en EN, pero «alamos» sigue ahí y el informe no cambia.

Los 2 avisos ES que quedan son reales: dos artículos publicados sobre Roswell
1947 sin canonical entre ellos, la misma canibalización que ya arreglamos en
el EN con el par Grusch. Queda pendiente de decisión editorial.
This commit is contained in:
ChemaVX
2026-07-21 07:15:40 +00:00
parent 7b92079c41
commit 31c9d3e7b8
+38 -1
View File
@@ -13,6 +13,7 @@ rule, write a function (post) -> list[Violation] and append it to RULES. The aud
and the validator both just call check_post(); they never re-implement a check. and the validator both just call check_post(); they never re-implement a check.
""" """
import re import re
import unicodedata
from collections import namedtuple from collections import namedtuple
SITE_HOST = "theexclusionzone.com" SITE_HOST = "theexclusionzone.com"
@@ -201,6 +202,33 @@ TOPIC_STOPWORDS = {
"unexplained", "encounter", "sighting", "sightings", "alien", "aliens", "unexplained", "encounter", "sighting", "sightings", "alien", "aliens",
"phenomena", "aerial", "unidentified", "extraordinary", "americas", "phenomena", "aerial", "unidentified", "extraordinary", "americas",
"american", "video", "footage", "american", "video", "footage",
# spanish glue (added 2026-07-21 with the ES site — zonadeexclusion.com).
# Without these, "que"/"los"/"del" counted as case identity: Kenneth Arnold
# 1947 "collided" with Roswell 1947 on nothing but «que» + the shared year.
# Cost: "los" no longer identifies Los Alamos on EN — "alamos" still does,
# and the EN corpus reports the same collisions before and after.
"los", "las", "una", "unos", "unas", "del", "por", "para", "con", "sin",
"sus", "que", "cual", "cuales", "quien", "quienes", "donde", "cuando",
"como", "pero", "porque", "aunque", "sobre", "entre", "hasta", "desde",
"tras", "ante", "bajo", "durante", "segun", "este", "esta", "esto",
"estos", "estas", "ese", "esa", "eso", "esos", "esas", "aquel", "aquella",
"otro", "otra", "otros", "otras", "todo", "toda", "todos", "todas",
"mismo", "misma", "cada", "algo", "alguien", "nada", "nadie", "mas", "muy",
"aun", "solo", "tambien", "siempre", "nunca", "jamas", "casi", "menos",
"fue", "fueron", "era", "eran", "ser", "son", "estan", "estaba",
"estaban", "haber", "habia", "han", "hay", "hizo", "hacer", "hace",
"tiene", "tienen", "tenia", "puede", "pueden", "podria", "sigue",
"siguen", "sabe", "dice", "dicen", "ano", "anos", "dia", "dias", "vez",
"veces", "despues", "antes", "hoy", "ahora",
# domain-generic ES — mirror of the English block above
"ovni", "ovnis", "fenomeno", "fenomenos", "caso", "casos", "incidente",
"incidentes", "misterio", "misterios", "expediente", "expedientes",
"archivo", "archivos", "documento", "documentos", "desclasificado",
"desclasificados", "desclasificacion", "gobierno", "militar", "militares",
"ejercito", "secreto", "secretos", "investigacion", "testigo", "testigos",
"avistamiento", "avistamientos", "encuentro", "encuentros",
"extraterrestre", "extraterrestres", "alienigena", "alienigenas",
"inexplicable", "inexplicables", "aereo", "aerea", "videos",
} }
# 0.70 calibrated 2026-07-10: the "When Nuclear Weapons Go / Arsenal Went # 0.70 calibrated 2026-07-10: the "When Nuclear Weapons Go / Arsenal Went
# Silent" near-twin pair scores 0.742 (char-level penalizes weapons/arsenal); # Silent" near-twin pair scores 0.742 (char-level penalizes weapons/arsenal);
@@ -215,8 +243,17 @@ NEWS_YEAR_MIN = 2020
_YEAR_RE = re.compile(r"\b(19|20)\d{2}\b") _YEAR_RE = re.compile(r"\b(19|20)\d{2}\b")
def _deaccent(text):
"""Fold accents to ASCII. Required for Spanish: the [a-z0-9]+ tokenizer
SPLITS on any accented char, so "Pentágono" became {pent, gono} and
"Fenómenos" became {fen, menos} — 3-char garbage that no stopword list can
ever cover, and that never matched the (already accent-free) Ghost slug.
No-op on EN, whose only non-ASCII are dashes and curly apostrophes."""
return unicodedata.normalize("NFKD", text).encode("ascii", "ignore").decode()
def _tokens(text): def _tokens(text):
return set(re.findall(r"[a-z0-9]+", _s(text).lower())) return set(re.findall(r"[a-z0-9]+", _deaccent(_s(text)).lower()))
def _case_years(title, slug): def _case_years(title, slug):