Not a simple table lookup: it decodes the way an HTML5 browser does — 2231 names, some valid without the semicolon, longest-prefix matching, Windows-1252 for €–Ÿ, and U+FFFD for references that cannot be characters.
html functionPython 3.4+Live demo
Common call
html.unescape('Tom & Jerry')
Returns
'Tom & Jerry'
Replaces
hand-written replace() chains and HTMLParser().unescape (removed in 3.9)
Watch out
it decodes references only — it does not remove tags
html.unescape(ss — Text containing character references. A str without any & is returned unchanged; bytes raise TypeError.type: str · required)
Only 106 old names (eacute, copy, amp, lt …) are in the table without a semicolon; hearts is not, so "&hearts" stays as it is. When a name is unknown, the longest known prefix wins: "¬it;" decodes the legacy name not and keeps "it;". References 128–159 are taken as Windows-1252 (150 is an en dash), 0, surrogates (D800–DFFF) and values above 10FFFF become U+FFFD (�), and most control characters, such as 7, are dropped. Names are case-sensitive: Lt; is ≪, not <.
Parameters
Name
Type
Required
Description
s
str
yes
Text containing character references. A str without any & is returned unchanged; bytes raise TypeError.
Return value
str — The text with every recognised reference replaced by its character(s). Unrecognised references are left as they are.
Common patterns
Clean up text from a feed or API
Titles and descriptions often arrive HTML-encoded.
Turning HTML-encoded text (scraped pages, RSS, API fields) back into plain text
Comparing or searching text that may contain references
Reach for something else
Removing tags → html.parser.HTMLParser (or a full parser library)
Percent-encoded URLs (%20) → urllib.parse.unquote
XML documents → xml.etree / xml.sax.saxutils.unescape (XML has only five named entities)
Notes
CPython impl
One regex, &(#[0-9]+;?|#[xX][0-9a-fA-F]+;?|[^\t\n\f <&#;]{1,32};?), substituted with a function that looks the match up in html.entities.html5 — see Lib/html/__init__.py
Numeric rules
Following the HTML5 tokenizer: 0x80–0x9F map through Windows-1252, 0 and anything that is not a valid code point give U+FFFD, noncharacters and most C0/C1 controls are removed
Huge numbers
A decimal reference longer than 4300 digits raises ValueError: int() refuses to convert it (the integer string conversion limit); hex references have no such limit
Inverse
unescape(escape(s)) == s always; escape(unescape(s)) == s only if s contained no references that escape() would not produce