52 lines
1.5 KiB
Python
52 lines
1.5 KiB
Python
"""HTML aus E-Mail-Artikeln sicher in lesbaren Text umwandeln.
|
|
|
|
Bewusst ohne externe Bibliothek und ohne Ausgabe von HTML: So können weder
|
|
Schadcode noch nachgeladene Bilder/Tracking-Pixel aus Support-Mails ausgeführt
|
|
werden. Block-Tags werden zu Zeilenumbrüchen, der Rest entfernt.
|
|
"""
|
|
import re
|
|
from html.parser import HTMLParser
|
|
|
|
_BLOCK = {"p", "div", "br", "li", "tr", "table", "blockquote", "h1", "h2",
|
|
"h3", "h4", "h5", "h6", "ul", "ol", "section", "article", "header"}
|
|
_DROP = {"script", "style", "head", "title"}
|
|
|
|
|
|
class _Extractor(HTMLParser):
|
|
def __init__(self):
|
|
super().__init__(convert_charrefs=True)
|
|
self.parts = []
|
|
self._skip = 0
|
|
|
|
def handle_starttag(self, tag, attrs):
|
|
if tag in _DROP:
|
|
self._skip += 1
|
|
elif tag in _BLOCK:
|
|
self.parts.append("\n")
|
|
|
|
def handle_endtag(self, tag):
|
|
if tag in _DROP and self._skip:
|
|
self._skip -= 1
|
|
elif tag in _BLOCK:
|
|
self.parts.append("\n")
|
|
|
|
def handle_data(self, data):
|
|
if not self._skip:
|
|
self.parts.append(data)
|
|
|
|
|
|
def clean_html(html: str) -> str:
|
|
if not html:
|
|
return ""
|
|
p = _Extractor()
|
|
try:
|
|
p.feed(html)
|
|
except Exception: # noqa: BLE001 - bei kaputtem HTML lieber Rohtext
|
|
return re.sub(r"<[^>]+>", "", html).strip()
|
|
text = "".join(p.parts)
|
|
# Mehrfache Leerzeichen/Leerzeilen zusammenfassen
|
|
text = re.sub(r"[ \t]+", " ", text)
|
|
text = re.sub(r" *\n *", "\n", text)
|
|
text = re.sub(r"\n{3,}", "\n\n", text)
|
|
return text.strip()
|