first commit
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
"""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()
|
||||
Reference in New Issue
Block a user