# Marcato

Source: https://getanalog.io/docs/marcato/

Convert HTML to Markdown while preserving document structure, links, code, and tables.

Marcato is the HTML-to-Markdown library used by Analog. It is also available
as a standalone Python package.

Use it when you already have HTML and need readable Markdown without
site-specific rules. Conversion runs in your Python process: Marcato does not
fetch pages or choose which part of a page to keep.

<details class="reference-index">
<summary>On this page</summary>

- [Install and convert](https://getanalog.io/docs/marcato/#install-and-convert)
- [What Marcato preserves](https://getanalog.io/docs/marcato/#what-marcato-preserves)
- [Resolve relative links](https://getanalog.io/docs/marcato/#resolve-relative-links)

</details>



## Install and convert

Install Marcato in your Python environment, then pass the HTML string
to `html_to_markdown()`. It returns the Markdown as a string.

`html_to_markdown()` accepts plain text, an HTML fragment, a full document, or
an empty string. It also handles malformed HTML when `lxml` can recover it.



### Install the package

```bash example=runnable scenario=marcato-install
pip install marcato
```

### Convert a heading and paragraph

```python example=runnable scenario=marcato-first-conversion
from marcato import html_to_markdown

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

```text example=output scenario=marcato-first-conversion
# Hello

Good to meet you!
```





## What Marcato preserves

Marcato renders headings, links, lists, quotes, code, images, media, callouts,
inline emphasis, and tables. It carries authored accessibility text
into the Markdown when an otherwise visual element would be silent.

Simple tables become
[GitHub Flavored Markdown tables](https://github.github.com/gfm/#tables-extension-).
When spanning cells, tables inside other tables, or block content cannot fit
that model, Marcato emits each cell in source order so the content is not lost.

Marcato's output is deterministic: the same input, Marcato version, and
supported `lxml` version produce the same output.



### Convert a table with a link and emphasis

```python example=runnable scenario=marcato-table-conversion
from marcato import html_to_markdown

html = """
<h2>Reading list</h2>
<table>
  <tr><th>Title</th><th>Status</th></tr>
  <tr>
    <td><a href="/guide/">Guide</a></td>
    <td><strong>Ready</strong></td>
  </tr>
</table>
"""
print(html_to_markdown(html))
```

```text example=output scenario=marcato-table-conversion
## Reading list

| Title | Status |
| --- | --- |
| [Guide](/guide/) | **Ready** |
```

The heading, column labels, link, and bold text remain in the Markdown.





## Resolve relative links

By default, link and media destinations remain as written in the HTML.
Pass the page's address as `base_url` when you want those relative
destinations resolved to absolute URLs.

A document's own `<base href>` takes precedence. Bare `#fragment`
links stay local to the document. Supplying `base_url` only changes
how destinations are written; Marcato does not fetch them.



### Give a relative link its full address

```python example=runnable scenario=marcato-relative-link
from marcato import html_to_markdown

html = '<a href="guide/">Read the guide</a>'
markdown = html_to_markdown(
    html,
    base_url="https://example.com/docs/",
)
print(markdown)
```

```text example=output scenario=marcato-relative-link
[Read the guide](https://example.com/docs/guide/)
```



The [complete Marcato contract on PyPI](https://pypi.org/project/marcato/#description)
documents its exact treatment of whitespace, block boundaries, code fences,
destinations, tables, compatibility, and attribution.