Plugin neuheiten: Web-Quellen-Crawler (4 Adapter, Rate-Limit, Dedup-Gate, Sync-UI)
This commit is contained in:
88
plugins/neuheiten/quellen/spielbox.py
Normal file
88
plugins/neuheiten/quellen/spielbox.py
Normal file
@@ -0,0 +1,88 @@
|
||||
"""Adapter für https://www.spielbox.de/neuheiten-neue-spiele (Joomla-Blog).
|
||||
|
||||
Seitenstruktur (Stand Analyse August 2026):
|
||||
- Pro Beitrag ein `div.item` mit Titel-Link in `div.page-header h2 a`,
|
||||
Beitragsdatum (TT.MM.JJJJ) im ersten Absatz (`p > strong`).
|
||||
- Pagination über `.pagination`-Links der Form `?start=N` (6 Beiträge je
|
||||
Seite) — alle Seiten werden durchlaufen, bis keine neue Seite mehr
|
||||
gefunden wird.
|
||||
- Verlag: Heuristik — Präfix des Titels vor dem Doppelpunkt
|
||||
(z. B. „Pegasus Spiele: Next Station Berlin“); fehlt er, bleibt der
|
||||
Verlag leer. Ein Autor wird auf dieser Liste nicht strukturiert
|
||||
mitgeliefert.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
from .basis import QuellenAdapter, QuellenTreffer
|
||||
|
||||
DATUM_MUSTER = re.compile(r"(\d{2}\.\d{2}\.\d{4})")
|
||||
|
||||
|
||||
class SpielboxQuelle(QuellenAdapter):
|
||||
name = "spielbox"
|
||||
anzeigename = "spielbox.de Neuheiten"
|
||||
start_urls = ("https://www.spielbox.de/neuheiten-neue-spiele",)
|
||||
|
||||
def seite_verarbeiten(self, inhalt: bytes, url: str):
|
||||
suppe = BeautifulSoup(inhalt, "html.parser")
|
||||
treffer: list[QuellenTreffer] = []
|
||||
for element in suppe.select("div.item"):
|
||||
kopf_link = element.select_one("div.page-header h2 a") or element.find("h2")
|
||||
if kopf_link is None:
|
||||
continue # Strukturänderung: Beitrag überspringen, Lauf hält an
|
||||
titel = kopf_link.get_text(" ", strip=True)
|
||||
if not titel:
|
||||
continue
|
||||
href = kopf_link.get("href") if hasattr(kopf_link, "get") else None
|
||||
quellen_url = _absolute_url(href, url) or url
|
||||
|
||||
datum = None
|
||||
for absatz in element.find_all("p"):
|
||||
strong = absatz.find("strong")
|
||||
if strong is not None:
|
||||
fund = DATUM_MUSTER.search(strong.get_text(strip=True))
|
||||
if fund:
|
||||
datum = fund.group(1)
|
||||
break
|
||||
|
||||
verlag = _verlag_aus_titel(titel)
|
||||
treffer.append(
|
||||
QuellenTreffer(
|
||||
titel=titel,
|
||||
verlag=verlag,
|
||||
autor=None,
|
||||
erscheinungsdatum_oder_quartal=datum,
|
||||
quellen_url=quellen_url,
|
||||
)
|
||||
)
|
||||
|
||||
folge_links: list[str] = []
|
||||
for link in suppe.select(".pagination a[href]"):
|
||||
ziel = _absolute_url(link["href"], url)
|
||||
# Nur echte Paginierung dieser Liste folgen (?start=N),
|
||||
# nicht den Druck-/Sprung-Links.
|
||||
if ziel and "start=" in ziel:
|
||||
folge_links.append(ziel)
|
||||
return treffer, folge_links
|
||||
|
||||
|
||||
def _verlag_aus_titel(titel: str) -> str | None:
|
||||
"""„Verlag: Spielname“ → Verlag als Präfix vor dem Doppelpunkt."""
|
||||
if ":" not in titel:
|
||||
return None
|
||||
praefix = titel.split(":", 1)[0].strip()
|
||||
if 0 < len(praefix) <= 60:
|
||||
return praefix
|
||||
return None
|
||||
|
||||
|
||||
def _absolute_url(href: str | None, basis_url: str) -> str | None:
|
||||
if not href:
|
||||
return None
|
||||
from urllib.parse import urljoin
|
||||
|
||||
return urljoin(basis_url, href)
|
||||
Reference in New Issue
Block a user