Skip to main content

htomd

Extract Markdown and metadata from HTML. Pure Python 3.12+, with no runtime dependencies or network access.

Installation

python -m pip install htomd

For the command line, install in an isolated tool environment:

uv tool install htomd

Python API

import htomd

html = "<article><h1>Hello</h1><p>Readable text.</p></article>"
markdown = htomd.convert(html)
document = htomd.extract(html, url="https://example.com/article")
print(document.markdown, document.metadata.title, document.diagnostics)

Pass decoded HTML strings. url resolves relative references. extract() returns immutable results; missing metadata is None. Empty content yields empty Markdown; invalid argument types raise TypeError.

Best suited to articles and documentation. Extraction can miss content or retain clutter. JavaScript, browser layout, math, and SVG are unsupported. Simple tables use GFM; complex tables become row/cell text.

Command line

Installing the package also installs the htomd command. To install a locally built wheel in a virtual environment:

python -m venv .venv
source .venv/bin/activate
python -m pip install dist/htomd-0.1.1-py3-none-any.whl

In Windows PowerShell, activate with .venv\Scripts\Activate.ps1 instead. See Contributing for the build commands.

curl -s https://example.com/article | htomd convert --url https://example.com/article
cat page.html | htomd convert > page.md
cat page.html | htomd extract > page.json

Both commands read stdin to EOF and accept no file arguments. Input must be UTF-8 (an optional UTF-8 BOM is accepted); output is UTF-8. --url supplies source context for relative references and metadata; it never fetches the URL.

convert writes Markdown unchanged. extract writes indented JSON containing markdown, metadata, and diagnostics, matching the Python result structure. Missing metadata is null; diagnostic notes are an array. Empty or malformed HTML receives the same best-effort handling as the library.

Run htomd, htomd help, or htomd --help for usage and pipeline examples. Use htomd help convert or htomd convert --help for command-specific help (likewise for extract). htomd version or htomd --version prints the installed package version. Help and version commands exit successfully without reading stdin.

python -m htomd supports the same commands. Successful conversion exits with code 0, I/O and decoding failures with code 1, and usage errors with code 2. Errors go to stderr.

Performance

Measured 2026-09-09 on Apple M5 Max (arm64, 36 GiB RAM), macOS-26.5.1-arm64-arm-64bit-Mach-O, Python 3.14.7. 115 offline sanitised pages, 12.82 MiB input. Single-threaded; five fresh rounds. Relative speed = competitor time / htomd time; above 1 means htomd is faster.

Library Conversion implementation Extra Python packages
htomd Pure Python / stdlib HTMLParser 0
markdownify Python / BeautifulSoup + stdlib HTMLParser 4
html2text Pure Python / stdlib HTMLParser 0
trafilatura Python + native C via lxml 16
html-to-markdown Rust core / Python bindings 0
htmd-py Rust core / Python bindings 0
Library Corpus seconds Round median range (s) Pages/s Input MiB/s Relative speed
htomd 0.1.0 1.260 1.245–1.283 91.3 10.17 1.00×
markdownify 1.2.3 2.443 2.418–2.465 47.1 5.25 1.94×
html2text 2025.4.15 1.262 1.239–1.271 91.1 10.15 1.00×
trafilatura 2.2.0 3.257 3.238–3.312 35.3 3.94 2.58×
html-to-markdown 3.12.2 0.217 0.216–0.226 529.6 59.02 0.17×
htmd-py 0.1.2 0.163 0.162–0.167 705.4 78.62 0.13×
Library Peak RSS MiB Import ms Wheel KiB Installed MiB
htomd 127.59 14.65 18.98 0.05
markdownify 174.22 58.61 15.36 0.78
html2text 81.25 9.18 33.84 0.10
trafilatura 257.05 276.78 148.35 58.21
html-to-markdown 131.98 10.71 6728.10 15.05
htmd-py 104.70 0.53 451.00 1.08

Dependency counts include all installed direct and transitive runtime packages, excluding the library itself, interpreter and installer tools. No optional extras were selected. Counts come from the saved isolated installations. Bundled native code and Rust/C libraries are not counted as Python packages: zero package dependencies does not mean pure Python.

Defaults perform different work: htomd.convert() selects content and extracts metadata; Trafilatura extracts main content with Markdown output selected. markdownify, html2text, and htmd-py convert markup; html-to-markdown returns content and metadata with its defaults. Output size and extraction coverage differ. These timings do not measure output quality.

RSS includes the interpreter, imports, decoded corpus and native allocations. Import time excludes interpreter startup; filesystem caches may be warm. Wheel size is the package alone; installed size includes transitive dependencies, excluding interpreter, installer tools and generated bytecode.

See methodology and reproduction, detailed results and failures, and raw measurements.

Development

mise trust
mise install
mise run setup
mise exec -- uv run --locked pytest

mise run check runs all checks. See CONTRIBUTING.md for hooks and releases.

MIT license, copyright 2026 JXD Ltd. Fixtures have separate licenses.

Release files for htomd 0.1.1

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

Source distribution (sdist)

Source distribution for htomd 0.1.1
File Size Uploaded
htomd-0.1.1.tar.gz 22.1 kB Details

Built distribution (wheel)

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

Total release size: 41.8 kB

Release files / htomd-0.1.1.tar.gz

Download URL htomd-0.1.1.tar.gz
Size 22.1 kB
Tags Source
SHA-256 checksum
How to use checksums
552d0c281e8fb97be99bcc3447410ed77d6c91b42b42134ad727881548b6d3d6
BLAKE2b-256 checksum
How to use checksums
1981fa9e361bab4891e3a38512abca532a99d04816af12783c37d7c69435c9ac
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.11 {"installer":{"name":"uv","version":"0.12.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / htomd-0.1.1-py3-none-any.whl

Download URL htomd-0.1.1-py3-none-any.whl
Size 19.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c6ddd1762a979a8066979d8ca0516d90d90ffe2fc23136f0812820809c41716a
BLAKE2b-256 checksum
How to use checksums
0b542e6d99f595aaf121e137b99374998af222ae60a83517a5ba23ffc8e541ae
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.11 {"installer":{"name":"uv","version":"0.12.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.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