python-markdownify
Convert HTML into clean, configurable Markdown from Python with fine-grained control over tags, escaping, and formatting.
Repository Health
Technical Analysis
Markdownify is a focused Python library that turns HTML into Markdown, exposing a simple markdownify() function alongside a subclassable MarkdownConverter class for anyone who needs more control than a one-shot conversion offers. It wraps BeautifulSoup for HTML parsing, then walks the resulting tree tag by tag, dispatching to a convert_<tagname> method for each element type, which makes the codebase easy to extend by simply overriding or adding methods on a subclass.
The library is opinionated about correctness at the edges: it ships options for heading style (ATX, ATX-closed, or underlined/Setext), bullet rotation across nested lists, strong/emphasis symbol choice, table header inference, and multiple escaping modes for characters that would otherwise be misread as Markdown syntax. This attention to escaping and edge-case handling is what distinguishes it from simpler HTML-stripping approaches, and it is why the project is a common dependency wherever HTML content (emails, CMS fields, scraped pages, LLM tool output) needs to become readable Markdown.
What You Get
- A
markdownify()function that converts an HTML string to Markdown in one call, with dozens of formatting options exposed as keyword arguments. - A
MarkdownConverterbase class with oneconvert_<tag>method per supported HTML element, so custom tag handling is a matter of overriding a single method in a subclass. - A
markdownifyCLI entry point for converting HTML files or piped stdin to Markdown from the shell, sharing the same option set as the Python API. - Configurable heading styles (ATX, ATX_CLOSED, UNDERLINED), bullet character rotation for nested lists, and a choice of
*or_for strong/emphasis symbols. - Multiple escaping modes (
escape_asterisks,escape_underscores,escape_misc) that prevent converted text from accidentally producing unintended Markdown syntax. - Support for converting directly from a
BeautifulSoupobject viaconvert_soup(), avoiding a redundant parse when the caller already has a soup tree.
Common Use Cases
- Converting HTML email bodies or CMS rich-text fields into Markdown for storage, diffing, or downstream rendering.
- Feeding scraped or fetched web page HTML into an LLM pipeline as clean Markdown context instead of raw markup.
- Building documentation tooling that needs to migrate HTML content (e.g. exported wiki pages) into a Markdown-based static site generator.
- Writing custom exporters that need selective control over which HTML tags are converted versus stripped entirely.
Under The Hood
Architecture
Markdownify is a small, single-module library centered on MarkdownConverter in markdownify/__init__.py. It parses input HTML with BeautifulSoup, then recursively walks the resulting tag tree, converting each node’s children first and passing the accumulated text plus a parent_tags set into a per-tag convert_<tagname> method (heading levels 1-6 are unified through a convert_hN dispatcher via a regex on the method name). This dispatch-by-naming-convention pattern is the core extension point: subclasses override or add convert_* methods to change or add tag behavior without touching the traversal logic itself. A separate main.py module wraps the same markdownify() entry point in an argparse CLI, keeping the library and CLI concerns cleanly split.
Tech Stack
The library targets Python and depends on beautifulsoup4 for HTML parsing (with a pluggable underlying parser via bs4_options, defaulting to the stdlib html.parser) and six for legacy Python 2/3 compatibility helpers, reflecting the project’s long history dating to 2012. Packaging uses a modern pyproject.toml with setuptools and setuptools_scm for version derivation from git tags, and the project ships a py.typed marker plus a hand-written .pyi stub file so consumers get static type checking despite the implementation itself not being fully type-annotated.
Code Quality
Tests are organized by concern across dedicated files (test_basic.py, test_conversions.py, test_tables.py, test_lists.py, test_escaping.py, test_advanced.py, test_args.py, test_custom_converter.py) and run with pytest through tox, alongside flake8 linting and restructuredtext-lint for the README. A second CI job runs mypy and mypy --strict against the stub-covered surface, giving the project real type-checking coverage even though the runtime code favors plain Python over inline type hints. Error handling is minimal by design — this is a text-transformation library with few failure modes beyond malformed input, which BeautifulSoup already tolerates.
What Makes It Unique
What sets Markdownify apart from simpler HTML-to-text strippers is its systematic handling of Markdown’s own syntax collisions: dedicated regexes escape sequences that would otherwise be misread as headings, list markers, or emphasis when they appear in converted text (e.g. a line starting with digits and a period, or a run of # characters). Combined with the override-one-method extension model, this makes it a common building-block dependency for projects that need correct, customizable Markdown output rather than a quick-and-dirty text extraction.
Used by 12 apps in this directory
QRev
AI Agents · CRM
Open source AI-first sales platform that replaces Salesforce with autonomous agents handling prospecting, outreach, and lead management at scale.
SurfSense
AI Assistants · Search
The open-source, unlimited NotebookLM alternative with real-time collaboration, a desktop app, and no vendor lock-in.
TensorZero
Ab Testing Experimentation · AI Development · Monitoring
TensorZero unifies the LLM gateway, observability, evaluation, optimization, and experimentation stack behind a single OpenAI-compatible API, built in Rust for sub-millisecond p99 latency.