Why convert HTML to Markdown?
Markdown has become the default writing format for documentation, READMEs, static site generators, wikis and note-taking apps. When content exists only as HTML (an old blog, a CMS export, a web page you want to keep), converting it by hand is slow. This HTML to Markdown converter, built on the Turndown library with GitHub-style extensions, produces tidy Markdown you can edit comfortably.
Example
<h1>Getting Started</h1>
<p>Install the <strong>CLI</strong> and run your <em>first</em> build.</p>
<ul><li>Fast</li><li><del>Slow</del></li></ul>
<pre><code class="language-bash">npm install -g my-cli</code></pre>
<table><tr><th>Flag</th><th>Meaning</th></tr><tr><td>-v</td><td>verbose</td></tr></table>
<p>See <a href="https://example.com">the docs</a>.</p># Getting Started
Install the **CLI** and run your _first_ build.
- Fast
- ~~Slow~~
```bash
npm install -g my-cli
```
| Flag | Meaning |
| --- | --- |
| -v | verbose |
See [the docs](https://example.com).
Style options
- Heading style – ATX (
# Heading) works for all levels and is the common choice. Setext underlines headings with===or---, but only exists for levels 1 and 2. - Bullet character –
-,*or+. They are equivalent; pick the one your linter or style guide prefers. - Code blocks – fenced blocks keep the language from a
language-*orlang-*class for syntax highlighting; indented blocks (four spaces) are the classic alternative. - Emphasis –
_underscores_or*asterisks*for italics. Bold always uses**.
How special content is handled
- Tables become GitHub-style pipe tables. Markdown tables cannot contain merged cells or block content, so complex tables are simplified.
- Strikethrough (
del,s,strike) becomes~~text~~. - Numbered lists keep a custom
startnumber, and nested lists are indented correctly. - Scripts, styles, noscript and templates are removed entirely.
- Elements with no Markdown equivalent, such as
divandspan, are reduced to their content. Classes, inline styles and colors are lost, because Markdown has no syntax for them. - Special characters that would otherwise trigger Markdown formatting, such as a leading
#or*in normal text, are escaped with a backslash.
Common use cases
- Migrating a blog from WordPress or another CMS to a static site generator such as Hugo, Jekyll, Astro or Eleventy, which store posts as Markdown files.
- Moving documentation from an HTML help system into a Git repository or a docs platform.
- Saving articles as notes in Markdown-based apps like Obsidian or Joplin.
- Preparing text for AI tools and chat apps, where Markdown keeps structure with far fewer characters than HTML.
Tips for converting web pages
- Convert only the main content (usually the
<article>or<main>element) to avoid navigation menus and footers in your Markdown. - Make image and link URLs absolute before converting if the Markdown will live somewhere else.
- If the source HTML is messy, run it through the HTML formatter first; well-structured HTML converts more cleanly.
- Finally, tidy the result with the Markdown formatter.
To check the result, preview it in the Markdown viewer, or convert it back with Markdown to HTML to compare. Conversion happens entirely in your browser.
Convert HTML to Markdown in Python
The standard library has no HTML to Markdown converter, so the usual choice is the markdownify package, which parses the HTML with Beautiful Soup and writes Markdown. Its defaults surprise people: h1 and h2 come out underlined with = and - characters instead of as # headings, and list items start with *. Pass heading_style="ATX" and bullets="-" to get the style most Markdown files use, which is also what this page produces by default.
# pip install markdownify
from markdownify import markdownify
html = """<h1>Getting Started</h1>
<p>Install the <strong>CLI</strong> and run your <em>first</em> build.</p>
<ul>
<li>Fast builds</li>
<li><a href="https://example.com/plugins">Plugin</a> support</li>
</ul>
<pre><code>npm install -g acme-cli</code></pre>"""
markdown = markdownify(html, heading_style="ATX", bullets="-")
print(markdown)
Paste the same HTML above to compare the two results, or turn the Markdown back with Markdown to HTML.