RSS-Parser: HTML aus summary strippen vor Speicherung

Ursache des Bugs: feedparser.entry.summary liefert bei vielen Quellen
(Guardian, AP, Sueddeutsche, Golem, Bellingcat, ...) HTML-kodierten Text
(<p>, <a>, <ul>, ...). Der Parser hat diesen 1:1 in articles.content_original
und content_de gespeichert. Folge:
- UI rendert HTML-Tags als Text in Timeline-Karten
- KI-Agenten (analyzer, entity_extractor, factchecker) bekommen HTML-Muell
  als Analyse-Input -> schwaechere Ergebnisse
- _is_german-Sprachheuristik wird durch Tags verzerrt
- 1000-Zeichen-Cap wird durch Tags + Tracking-URLs verbraucht

Fix: html_to_text aus feeds/transcript_extractors/_common.py wiederverwenden,
strippt Tags + decodiert HTML-Entities (inkl. dt. Umlaute) + normalisiert
Whitespace. Wird auf summary direkt nach entry.get angewandt -> betrifft
sowohl Match-Logik (text-Variable) als auch INSERT (content_original/de).

Backfill-Migration: migrations/migrate_html_strip_2026-05-03.py im
Verwaltungs-Repo, behandelt bestehende DB-Eintraege rueckwirkend.
Dieser Commit ist enthalten in:
Claude Code
2026-05-02 23:13:32 +00:00
Ursprung 430541f49b
Commit 307f0a1868

Datei anzeigen

@@ -6,6 +6,7 @@ import httpx
from datetime import datetime, timezone from datetime import datetime, timezone
from config import TIMEZONE, MAX_ARTICLES_PER_DOMAIN_RSS from config import TIMEZONE, MAX_ARTICLES_PER_DOMAIN_RSS
from source_rules import _extract_domain from source_rules import _extract_domain
from feeds.transcript_extractors._common import html_to_text
logger = logging.getLogger("osint.rss") logger = logging.getLogger("osint.rss")
@@ -152,7 +153,11 @@ class RSSParser:
for entry in feed.entries[:50]: for entry in feed.entries[:50]:
title = entry.get("title", "") title = entry.get("title", "")
summary = entry.get("summary", "") # RSS-summary ist bei vielen Quellen HTML (Guardian, AP, SZ, ...).
# Vor weiterer Verwendung strippen, sonst landet HTML in DB
# und KI-Agenten und Sprach-Heuristik werden gestoert.
summary_raw = entry.get("summary", "")
summary = html_to_text(summary_raw) if summary_raw else ""
text = f"{title} {summary}".lower() text = f"{title} {summary}".lower()
# Adaptive Match-Schwelle: # Adaptive Match-Schwelle: