html
escape() makes text safe to drop into an HTML page or a quoted attribute; unescape() decodes named and numeric references exactly the way a browser does. Everything else lives in the html.parser and html.entities submodules.
import html from html import escape, unescape
Demo
import html html.escape('<script>alert("hi")</script>')
escape() replaces & first, so text that already contains a reference gets a second layer: "Fish & Chips" written as a reference comes out with its & escaped again. unescape() follows the HTML5 rules: a few legacy names such as amp and not work without the semicolon (so "¬it;" becomes "¬it;"), but most names, like hearts, need it; references 128-159 are read as Windows-1252, so – is an en dash and ™ is ™; and � becomes U+FFFD (�).
Members
Common patterns
import html row = f"<td>{html.escape(comment)}</td>"
import html link = f'<a href="{html.escape(url)}" title="{html.escape(title)}">'
import html text = html.unescape(fragment_without_tags)
from html.parser import HTMLParser class Links(HTMLParser): def handle_starttag(self, tag, attrs): if tag == "a": print(dict(attrs).get("href")) Links().feed(page)
Examples
Pitfalls
import html html.escape(html.escape('Fish & Chips'))
import html html.escape('Fish & Chips')
import html v = 'x" onmouseover="alert(1)' f'<a title="{html.escape(v, quote=False)}">'
import html v = 'x" onmouseover="alert(1)' f'<a title="{html.escape(v)}">'
import html html.unescape('<b>bold</b>')
from html.parser import HTMLParser class Text(HTMLParser): def __init__(self): super().__init__() self.parts = [] def handle_data(self, data): self.parts.append(data) p = Text() p.feed('<b>bold</b> & plain') ''.join(p.parts)
When to use
- Generating HTML by hand (f-strings, small reports, emails) with values you do not control
- Decoding references in scraped text, RSS titles or API fields that arrive HTML-encoded
- Full pages → a template engine with auto-escaping (Jinja2, Django templates)
- Sanitizing HTML you want to keep partly (allow <b>, drop <script>) → a dedicated sanitizer library
- URLs in query strings → urllib.parse.quote; JSON in a <script> → json.dumps plus escaping of </
Notes
FAQ
import html, then html.escape(text). It replaces &, < and > with the named references amp, lt and gt (each written between & and ;), and with the default quote=True also " (quot) and ' (the hex reference '). Do it once, at the moment the text goes into the page.