html.entities: Convert HTML Entities

Published on: August 21, 2026
Reading time: 6 minutes
HTML keycaps representing HTML entities with Python html.entities

The html.entities module in Python’s standard library contains reference tables that connect HTML entity names, Unicode characters, and code points. It is useful when an application needs to understand references such as &, ©,  , or ☃, generate entity documentation, verify conversions, or build text-processing tools.

The module is not a complete HTML parser and it does not sanitize content. Its main role is to expose reference data. To process tags, attributes, comments, and text nodes, read the guide to Python html.parser. To convert an entire string containing HTML references, html.unescape() is usually the simpler high-level choice.

What an HTML entity is

An entity is a textual representation of a character. In HTML, an ampersand begins a character reference. A named reference uses an identifier, such as < for the less-than sign. A numeric reference uses a decimal or hexadecimal Unicode code point, such as < or <.

Character references exist because some characters have a special meaning in markup or may be inconvenient to type. However, every character does not need to become an entity. Modern HTML and UTF-8 can represent most symbols directly. The correct representation depends on the output context.

The module’s four dictionaries

The official documentation describes four main mappings:

  • html.entities.html5 maps HTML5 named character references to their Unicode text.
  • html.entities.name2codepoint maps HTML4 names to Unicode code points.
  • html.entities.codepoint2name maps Unicode code points back to HTML4 names.
  • html.entities.entitydefs contains XHTML 1.0 entity definitions and replacement text.

These mappings are not interchangeable. HTML5 contains more names, and some references expand to more than one Unicode character. Select the table that matches the document standard and your purpose.

Looking up HTML5 entities

from html.entities import html5

print(html5["copy;"])
print(html5["nbsp;"])
print(html5["NotEqualTilde;"])

Keys in html5 commonly include the trailing semicolon. Some references accepted by the standard also appear without it. Do not automatically remove the semicolon before lookup because its absence can change parsing behavior in particular contexts.

A value may contain one or several Unicode characters. Never assume that len(value) is always one. That distinction matters when calculating positions, truncating text, highlighting matches, or creating a reverse index.

Searching entities by name

from html.entities import html5

prefix = "copy"
matches = {
    name: value
    for name, value in html5.items()
    if name.lower().startswith(prefix)
}

for name, value in matches.items():
    print(name, repr(value))

This pattern can support documentation pages, editor completion, or educational tools. In a web application, limit the number of returned items and never turn user-supplied names into raw HTML without contextual escaping.

Using name2codepoint

from html.entities import name2codepoint

codepoint = name2codepoint["euro"]
character = chr(codepoint)

print(codepoint)
print(character)

name2codepoint returns integers. The built-in chr() function converts a code point into a Unicode string. The reverse mapping is available through codepoint2name:

from html.entities import codepoint2name

name = codepoint2name.get(ord("©"))
print(name)

Use get() when a missing name is expected. Many Unicode characters have no named HTML4 entity. Your application can preserve the character, emit a numeric reference, or follow an explicit fallback policy.

Creating numeric references

def decimal_reference(character: str) -> str:
    if len(character) != 1:
        raise ValueError("Provide exactly one character")
    return f"&#{ord(character)};"

print(decimal_reference("☃"))

For hexadecimal output, use f"&#x{ord(character):X};". Numeric references work for a wider range of characters, but they should still be produced according to context. Encoding every character as a reference makes documents larger and harder to read without automatically improving security.

When to use html.escape and html.unescape

The parent html module provides higher-level functions. html.escape() protects special characters when inserting plain text into basic HTML content. html.unescape() interprets named and numeric character references using HTML rules.

import html

text = "Tom & Jerry <3 programming"
escaped = html.escape(text)
restored = html.unescape(escaped)

print(escaped)
print(restored)

html.entities is the better fit when you need to inspect tables, understand a particular name, build an index, or implement a controlled conversion. For ordinary whole-string encoding and decoding, prefer the tested high-level functions.

Decoding is not sanitizing

This is the most important security distinction. Calling html.unescape("&lt;script&gt;") produces a string that contains a script tag. The function only converts references; it does not decide whether the result is safe to render.

If untrusted content must appear as text, escape it at the output boundary. If an application allows a subset of HTML, use a maintained sanitization library with an allowlist of tags and attributes. Do not attempt to build a sanitizer by stripping entities, using regular expressions, or blocking a handful of words.

Entities inside attributes

Attribute contexts have additional rules. A value inserted into href, src, style, or an event attribute cannot be protected merely by replacing angle brackets. URLs require scheme and origin validation, and dangerous attributes should not be accepted from untrusted authors.

For URL decomposition, the guide to Python urllib.parse explains components and encoding. Parsing a URL still does not make it trustworthy.

Optional semicolons

HTML5 accepts some named references without a semicolon under specific historical compatibility rules. The html5 table can contain both forms. When generating new HTML, use the semicolon. It is clearer and prevents ambiguity with letters or digits that follow the entity name.

from html.entities import html5

print("amp;" in html5)
print("amp" in html5)

When processing existing documents, let a standards-aware parser apply the rules. A custom routine that only searches from an ampersand to the next semicolon will fail on incomplete or ambiguous markup.

References with multiple characters

Some HTML5 references represent Unicode sequences rather than a single code point. Code that assumes a one-to-one relationship may truncate output or calculate the wrong index. Preserve the full string and include complex entities in tests.

from html.entities import html5

for name, value in html5.items():
    if len(value) > 1:
        print(name, [f"U+{ord(char):04X}" for char in value])
        break

This also interacts with Unicode normalization. Two visually similar strings can use different sequences. For comparison and search, consider unicodedata.normalize(), described in the guide to Python unicodedata.

Building an entity catalog

from html.entities import html5

catalog = []
for name, value in sorted(html5.items()):
    catalog.append({
        "name": name,
        "text": value,
        "codepoints": [f"U+{ord(char):04X}" for char in value],
    })

print(catalog[:3])

This catalog can be exported to JSON, CSV, or a documentation page. When creating HTML output, escape both labels and values through the template system. Even trusted standard-library data should travel through the correct output mechanism so future code changes do not accidentally introduce raw markup.

Handling unknown names

from html.entities import html5

def resolve(name: str) -> str | None:
    key = name if name.endswith(";") else name + ";"
    return html5.get(key)

print(resolve("copy"))
print(resolve("missing_entity"))

Do not silently replace an unknown entity with an empty string because that loses information. Preserve the source, return None, log a bounded warning, or reject the input according to the application contract.

Performance considerations

The tables are already dictionaries and key lookups are fast. Most applications should not copy the entire mapping repeatedly. Import once, reuse the objects, and create additional indexes only when a measured workload needs them.

For large documents, avoid running one replacement pass per entity. Use html.unescape() or an incremental parser. The html.parser guide demonstrates feeding input in chunks.

Test common names, decimal and hexadecimal numeric references, optional semicolons, values containing several characters, unknown names, already escaped text, and malformed input. Test the final output context as well: text content, attributes, JSON, logs, or a database field.

A test should verify more than the resulting character. It should also verify the security policy. A correctly decoded string may still be unsafe to render as HTML.

Common mistakes

Frequent mistakes include using the HTML4 mapping for HTML5 content, assuming every value is one character, forgetting the semicolon, discarding unknown references, treating unescape() as a sanitizer, escaping too early and then double-escaping, and placing decoded values in URLs or attributes without contextual validation.

Conclusion

html.entities gives direct access to the relationships between HTML names and Unicode. It is valuable for catalogs, analysis tools, specialized converters, and validation. For common tasks, combine it with html.escape(), html.unescape(), and an appropriate parser.

Read the official html.entities documentation and the HTML named character reference list. Keep decoding, parsing, validation, escaping, and sanitization as separate operations.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Folder with files representing MIME types identified with Python mimetypes
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    mimetypes: Detect MIME Types for Files

    Learn Python mimetypes to identify file types, validate uploads, and set safer HTTP Content-Type headers.

    Ler mais

    Tempo de leitura: 6 minutos
    20/08/2026
    HTML code on a screen representing parsing with Python html.parser
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python html.parser: Parse HTML

    Learn Python html.parser to extract text, links, and metadata, process HTML incrementally, and avoid confusing parsing with sanitization.

    Ler mais

    Tempo de leitura: 5 minutos
    20/08/2026
    Server rack representing low-level HTTP connections with Python http.client
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python http.client: Low-Level HTTP

    Learn Python http.client for low-level HTTP and HTTPS connections, streaming, headers, TLS, connection reuse, size limits, and errors.

    Ler mais

    Tempo de leitura: 4 minutos
    20/08/2026
    HTML code on a screen representing responsible crawling with Python robotparser
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python robotparser: Read robots.txt

    Learn Python urllib.robotparser to respect robots.txt, crawl-delay, request-rate, sitemaps, caching, and responsible crawling limits.

    Ler mais

    Tempo de leitura: 4 minutos
    20/08/2026
    Keyboard tiles spelling HTTP representing Python urllib.request requests
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python urllib.request: Native HTTP

    Learn Python urllib.request for GET, POST, JSON, downloads, TLS, redirects, proxies, size limits, and robust HTTP error handling.

    Ler mais

    Tempo de leitura: 4 minutos
    19/08/2026
    Ethernet cables connected to a network switch representing Python socketserver services
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python socketserver: Build Servers

    Learn Python socketserver to build TCP and UDP servers with handlers, concurrency, framing, limits, timeouts, and graceful shutdown.

    Ler mais

    Tempo de leitura: 5 minutos
    19/08/2026