Skip to main content

Marcato

Marcato by Analog converts HTML to Markdown with one parser and one narrow dependency: lxml.

pip install marcato
from marcato import html_to_markdown

html = "<h1>Hello</h1><p>Good to meet you!</p>"
markdown = html_to_markdown(html)
print(markdown)
# Hello

Good to meet you!

Marcato preserves common document structure including headings, links, lists, tables, code, images, advisory blocks, and authored inline emphasis. Its single rendering policy defines an intentional GFM-compatible presentation.

Input and rendering contract

html_to_markdown() accepts plain text, an HTML fragment, a full HTML document, or an empty string. Malformed HTML remains supported when lxml can recover it; unrecoverable markup raises ValueError. Given the same input, Marcato version, and supported lxml version, recovery and rendering are deterministic. Non-string input raises TypeError.

Presentation

Marcato uses ATX headings (# through ######), one stable - marker for unordered lists at every depth, inline links, and unwrapped prose. Literal asterisks and underscores are escaped, while broad punctuation escaping stays off; generated links, images, tables, and other Markdown constructs always escape the grammar boundaries they own.

Normal-flow horizontal whitespace collapses across inline child boundaries, including around omitted hidden content and text synthesized from authored accessibility metadata. Browser-collapsible whitespace at the document edges is omitted. Preformatted content, Markdown indentation, and interior hard line breaks remain unchanged.

Blocks and regions

Marcato preserves boundaries between standard HTML block containers, including main, article, section, aside, header, footer, nav, figure, and address. These containers are structurally transparent: they separate their authored contents without adding labels or deciding which page regions matter. Callers remain responsible for selecting the HTML region Marcato receives.

Block containers authored inside a link retain their separation without terminating the Markdown link label. Phrasing containers such as span, abbr, and time remain inline and do not introduce new boundaries.

Code languages

For a block <pre> element, Marcato emits a syntax-highlighting language when it has one direct <code> child whose classes contain exactly one language-TOKEN entry. TOKEN must begin with an ASCII letter or digit and may then contain ASCII letters, digits, _, ., +, #, or -. Other class names are not interpreted as languages, and unsafe or ambiguous values are omitted.

Marcato chooses backtick or tilde fences that cannot collide with the code content.

By default, Marcato serializes authored link, image, and video destinations without interpreting or rewriting them. It retains compact inline Markdown for simple destinations and uses the equivalent angle-bracket form when whitespace, control characters, or unbalanced parentheses would otherwise corrupt the construct. Titles, link text, and image alt text are escaped at their Markdown grammar boundaries so page-authored characters cannot replace a destination or terminate a link early.

Starting with Marcato 0.4.0, pass the address the HTML was served from as base_url to resolve relative link, image, video, and poster destinations:

markdown = html_to_markdown(
    '<a href="reports.html">Reports</a>',
    base_url="https://example.com/ir/",
)

A document's own <base href> takes standard precedence, and bare #fragment anchors remain local to the document. Omitting base_url preserves the default behavior above.

Matching link text is emitted as an autolink only when the destination is an eligible absolute URI. Relative and empty destinations retain ordinary inline link syntax. Marcato does not allowlist, fetch, or assess destinations.

An icon-only local link whose fragment target is declared by its surrounding heading is treated as that heading's permalink affordance. Marcato keeps the heading text once instead of synthesizing a duplicate link from accessibility metadata. Visible heading links and icon-only links to other targets remain authored content.

When a local link that remains in the Markdown points to a custom target declared by a heading or its descendants, Marcato preserves that relationship with a GFM custom anchor immediately before the heading. Unreferenced IDs, targets outside headings, and suppressed permalink affordances add no anchor markup.

Tables

Marcato emits a GFM table when its cells contain inline content, it has at most one header row, and it does not use spanning or nested tables. Pipes in cell content are escaped, <br> remains an inline break, and shorter rows receive empty trailing cells so every authored value remains in the table. Headerless tables receive an empty header rather than assigning header semantics to the first authored row.

A table with colspan, rowspan, multiple header rows, block content in a cell, or another table inside it cannot be represented faithfully as a GFM table. Marcato emits its caption followed by each non-empty cell as ordinary Markdown blocks in source order. This linear fallback preserves authored content without inventing a rectangular relationship. A page-authored accessible name still fills an otherwise silent cell.

Compatibility

Marcato's rendered Markdown is its primary public contract. Within a 0.x minor line, patch releases do not intentionally change rendered bytes for an existing HTML input. Intentional rendering changes, including bug fixes that alter output, begin a new minor line.

The same Marcato and lxml versions produce deterministic output. Marcato bounds the lxml major versions it supports; dependency-induced output drift within that range is treated as a compatibility defect.

Attribution

Marcato originated as an lxml port of markdownify 1.2.3 by Matthew Tretter and has since diverged. The complete upstream copyright and MIT license notice ships in THIRD-PARTY-NOTICES.md.

Marcato itself is licensed under the MIT License.

Metadata

Release files for marcato 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for marcato 0.4.0
File Size Uploaded
marcato-0.4.0.tar.gz 41.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for marcato 0.4.0
File Interpreter ABI Platform
marcato-0.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 71.7 kB

Release files / marcato-0.4.0.tar.gz

Download URL marcato-0.4.0.tar.gz
Size 41.4 kB
Tags Source
SHA-256 checksum
How to use checksums
56ff6661e0625b850c12f586b7fd60fc1fdd1b42a4b27c1a085fc3adc107562d
BLAKE2b-256 checksum
How to use checksums
7e34b600f2d84075728ea0d667574f4224d8cd4658462d867b37b92a7e6882a5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 17, 2026.

Transparency log

Release files / marcato-0.4.0-py3-none-any.whl

Download URL marcato-0.4.0-py3-none-any.whl
Size 30.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
94a8e62a39d992729bbd7d7c5789067f36b0997a6c292ac09793f7b118711c60
BLAKE2b-256 checksum
How to use checksums
039c166a11f7427a0cc677a7c7585a8b8178bee1a6ace55997cc57c031bc1647
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 17, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.4.0 This release

2 release files

0.3.0

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page