Cliquenabend: Release-Feld der Spieldetailseiten parsen (echtes Erscheinungsjahr statt Update-Datum)

This commit is contained in:
Flo Hartmann
2026-08-25 00:44:31 +00:00
parent aea52d1563
commit 199f8b080d
2 changed files with 85 additions and 5 deletions

View File

@@ -10,12 +10,17 @@ Die Seite ist nicht paginiert.
"""
from __future__ import annotations
import logging
import re
from urllib.parse import urljoin
from bs4 import BeautifulSoup
from bs4.element import Tag
from .basis import QuellenAdapter, QuellenTreffer, bild_aus_element
_logger = logging.getLogger(__name__)
DATUM_MUSTER = re.compile(r"\d{2}\.\d{2}\.(\d{2})\b")
BASIS_URL = "https://www.cliquenabend.de"
@@ -40,6 +45,42 @@ class CliquenabendQuelle(QuellenAdapter):
treffer.append(self._zeile(zellen, url))
return [t for t in treffer if t is not None], []
def _release_jahr(self, spiel_url: str) -> str | None:
"""Liest das Release-Feld von der Spieldetailseite.
Die Detailseite (…/spiele/….html) enthält ein Feld
`<b>Release:</b><br> JAHR <br>` — meist nur das Jahr (z. B.
„2025“), gelegentlich ein genaues Datum. Liefert die rohe Angabe
zurück; `jahr_aus_datumsangabe` leitet daraus später das Jahr ab.
Fehler (404, Strukturänderung) führen zu None und blockieren nie.
"""
try:
antwort = self.client.hole(spiel_url)
except Exception:
_logger.warning("Cliquenabend: Detailseite nicht erreichbar (%s)", spiel_url)
return None
if antwort.status_code != 200:
return None
suppe = BeautifulSoup(antwort.content, "html.parser")
fett = suppe.find(
"b", string=lambda s: isinstance(s, str) and s.strip() == "Release:"
)
if not isinstance(fett, Tag):
return None
# Wert steht direkt hinter dem Label (<br>-getrennt); der nächste
# String-Knoten nach dem Label-Element (nicht darin) ist der Wert.
text = fett.next_sibling
while isinstance(text, Tag):
text = text.next_sibling
while isinstance(text, str) and not text.strip():
text = text.next_sibling
while isinstance(text, Tag):
text = text.next_sibling
if not isinstance(text, str):
return None
wert = text.strip()
return wert or None
def _zeile(self, zellen, url: str) -> QuellenTreffer | None:
spiel_zelle = zellen[3]
titel_link = spiel_zelle.find("a", href=re.compile(r"^/spiele/"))
@@ -50,8 +91,6 @@ class CliquenabendQuelle(QuellenAdapter):
titel = (fett or titel_link).get_text(" ", strip=True)
href = titel_link.get("href")
if href:
from urllib.parse import urljoin
quellen_url = urljoin(BASIS_URL, href)
if not titel:
return None
@@ -66,6 +105,15 @@ class CliquenabendQuelle(QuellenAdapter):
# Datum so übernehmen, wie es in der Quelle steht (z. B. „15.10.25“).
datum = fund.group(0)
# Release-Feld der Detailseite hat Vorrang: dort steht das echte
# Erscheinungsjahr/-datum statt des Update-Datums der Mese-Tabelle.
if titel_link is not None:
href = titel_link.get("href")
if href:
release = self._release_jahr(urljoin(BASIS_URL, href))
if release:
datum = release
return QuellenTreffer(
titel=titel,
verlag=verlag,