Skip to content

Repository files navigation


𖤐 domonic 𖤐

The browser DOM, in Python.

Generate HTML. Parse real pages. Query with CSS or XPath. Manipulate a browser-style DOM.
User and learn real HTML, DOM and JavaScript-style APIs using Python code!

PyPI version Downloads Python version Python package Documentation License: MIT GitHub stars


domonic is a pure-Python DOM.

from domonic.html import *

page = html(
    body(
        h1("Hello, World!"),
        p("HTML as Python objects."),
        a("GitHub", _href="https://github.com")
    )
)

print(page)
<html><body><h1>Hello, World!</h1><p>HTML as Python objects.</p><a href="https://github.com">GitHub</a></body></html>

But generating HTML is only the beginning.

heading = page.querySelector("h1")
heading.textContent = "Hello, DOM!"

for link in page.querySelectorAll("a"):
    print(link.href)

The same kind of DOM can also come from parsed HTML.

from domonic import domonic

document = domonic.parseString("""
<html>
    <body>
        <h1>Hello</h1>
        <a href="/docs">Documentation</a>
    </body>
</html>
""")

print(document.querySelector("h1").textContent)

Copy-paste recipes

Scrape links like Beautiful Soup, keep a real DOM

from domonic.bs4 import BeautifulSlop

soup = BeautifulSlop("<main><a href='/docs'>Docs</a></main>", "html.parser")

for link in soup.find_all("a", href=True):
    print(link.text, link["href"])

# Same object, still domonic:
print(soup.querySelector("a").getAttribute("href"))

Build a server-side component

from domonic.html import a, article, h2, p

def card(title, body, href):
    return article(h2(title), p(body), a("Open", _href=href), _class="card")

print(card("Python DOM", "Generate HTML with Python objects.", "/docs"))

Stream large HTML responses

from fastapi.responses import StreamingResponse
from domonic.html import body, html, table, td, tr

def rows():
    for index in range(50000):
        yield tr(td(f"Row {index}"), td(f"Data {index}"))

page = html(body(table(rows())))

return StreamingResponse(page.stream(), media_type="text/html")

Sanitize user HTML

from domonic.webapi.sanitizer import Sanitizer

clean = Sanitizer().sanitizeToString(
    '<p onclick="bad()">Hello <script>bad()</script></p>'
)
print(clean)

Send a minimal DOM patch

from domonic.diffdom import DiffDOM
from domonic.html import div, p

old = div(p("Version one"))
new = div(p("Version two"))

changes = DiffDOM().diff(old, new)
print(changes)

More focused guides:


Features

🏗️ Markup generation HTML5, SVG, XML, MathML, RSS, Atom, ODF, A-Frame, X3D and custom elements
🌳 DOM Document, Element, Node, NodeList, fragments, ranges, events, traversal, observers, shadow DOM and more
🔎 Querying CSS selectors and XPath
📥 Parsing Multiple interchangeable parser backends
🌐 Web APIs URL, URLPattern, storage, messaging, workers, crypto, performance, permissions and more
🟨 JavaScript-like APIs Array, Date, Math, String, Number, Promise, timers, typed arrays and JSON helpers
CLI Query URLs, files or piped HTML with CSS and XPath
🧪 Experiments dQuery, d3-inspired utilities, diffdom, BeautifulSlop and other browser-inspired ideas

Not every API is implemented to browser-complete parity — the goal is to keep moving closer to the real standards. Python 3.10+.


Install

python3 -m pip install domonic

Upgrade:

python3 -m pip install --upgrade domonic

For the domonic command line tool, pipx keeps the executable isolated and on your shell path:

brew install pipx
pipx ensurepath
pipx install domonic
domonic -x https://example.com '//title'

Then:

from domonic.html import *

print(h1("hello world"))

HTML that is actually Python

HTML elements are ordinary Python objects.

The tag names are the HTML names. The attribute names are the HTML names with a Python-friendly leading underscore where needed. That means examples often read like HTML with Python syntax:

from domonic.html import *

card = div(
    h2("domonic"),
    p("The browser DOM, in Python."),
    a("Documentation", _href="https://domonic.readthedocs.io"),
    _class="card"
)

print(card)

Attributes are prefixed with _ to avoid collisions with Python keywords:

label("Email", _for="email", _class="label")
<label for="email" class="label">Email</label>

For attributes that cannot be expressed as Python identifiers:

div(
    "hello",
    **{"_data-user-id": "42"}
)

A real DOM

domonic elements are more than formatted strings.

They're nodes in a document tree.

from domonic.html import *
from domonic.dom import document

page = html(
    body(
        main(
            h1("Projects"),
            ul(
                li("domonic"),
                li("Blueberry"),
                li("ezcron")
            )
        )
    )
)

print(page.querySelector("h1"))
print(page.querySelectorAll("li"))

Manipulate the tree using familiar DOM concepts:

title = page.querySelector("h1")
title.textContent = "Open source projects"

new_item = document.createElement("li")
new_item.textContent = "something new"

page.querySelector("ul").appendChild(new_item)

The project aims to follow the real platform where practical:

See the DOM documentation for the implemented API.


CSS selectors

Use browser-style selectors directly against the tree.

page.querySelector("button")
page.querySelector("#content")
page.querySelector(".active")

page.querySelectorAll("a")
page.querySelectorAll("a[rel=nofollow]")
page.querySelectorAll("a[href='#services']")
page.querySelectorAll("a[href$='technology']")
page.querySelectorAll("a[href*='github']")
for link in page.querySelectorAll("a"):
    print(link.href)

XPath

XPath is available too.

From Python:

from domonic import domonic

page = domonic.parseString("<main><h1>Hello</h1></main>")

# use XPath against your document tree

Or straight from your terminal:

domonic -x https://example.com '//a'

Against a local file:

domonic --xpath-file ./page.html '//title'

Or pipe HTML directly into it:

curl -s https://example.com | domonic -x '//a' --count

Parse HTML

from domonic import domonic

page = domonic.parseString("""
<!doctype html>
<html>
    <body>
        <article>
            <h1>Hello from HTML</h1>
        </article>
    </body>
</html>
""")

print(page.querySelector("h1"))

You can also load a page through the window API:

from domonic.window import window

window.location = "https://example.com"

print(window.document.title)

Pick your parser

One parser does not fit every job.

domonic lets you choose between zero dependencies, pure Python compatibility, malformed-HTML repair and high-performance native parsers.

from domonic import domonic

page = domonic.parseString("<p>Hello</p>", parser="selectolax")
page = domonic.parseString("<p>Hello</p>", parser="turbohtml")
page = domonic.parseString("<p>Hello</p>", parser="lxml_html")
page = domonic.parseString("<p>Hello</p>", parser="markupever")
page = domonic.parseString("<p>Hello</p>", parser="html5_parser")
page = domonic.parseString("<p>Hello</p>", parser="html.parser")
page = domonic.parseString("<p>Hello</p>", parser="html5lib")
page = domonic.parseString("<p>Hello</p>", parser="expat")
page = domonic.parseString("<p>Hello</p>", parser="justhtml")

The default is parser="auto", which picks the fastest installed backend that can parse the input (trying selectolax, turbohtml, lxml_html, html5_parser, markupever, html.parser, justhtml, then html5lib). Call domonic.get_active_parser() afterwards to see which one ran.

Set one for your application:

from domonic import domonic

domonic.set_default_parser("html.parser")

page = domonic.parseString("<p>Hello</p>")

Parser choices

Fastest on the bundled large-page benchmark first:

Parser Why use it?
selectolax Fast native HTML parsing with direct domonic DOM adaptation
turbohtml Fast native WHATWG parsing with direct domonic DOM adaptation
lxml_html Very fast lxml-backed parsing and direct lxml DOM adaptation
html5_parser Fast HTML5 parsing through the shared lxml DOM adapter
markupever Fast Rust-powered HTML repair; uses the shared lxml DOM adapter
html.parser Python standard library; no extra dependency
justhtml Pure-Python alternative with a direct domonic DOM adapter
html5lib Pure Python and bundled with domonic
expat Built into Python; useful for XML-like input

Optional parsers require their respective packages.

Install the native parser stack like this:

python -m pip install selectolax
python -m pip install turbohtml
python -m pip install lxml
python -m pip install markupever lxml
python -m pip install html5-parser lxml

For parser details and installation notes, see the parser performance guide.


Render it back to markup

Every element can be rendered with str():

from domonic.html import *

page = div(
    h1("Hello"),
    p("Rendered from a Python DOM.")
)

markup = str(page)

print(markup)

Write documents to disk with render:

render(f"{page}", "index.html")

Rendering behaviour can be configured through DOMConfig.

from domonic.dom import DOMConfig

print(DOMConfig.GLOBAL_AUTOESCAPE)
print(DOMConfig.RENDER_OPTIONAL_CLOSING_TAGS)

See the DOM documentation for all rendering options.


Browser-flavoured Python

domonic includes a large practical slice of JavaScript's familiar APIs.

from domonic.javascript import Math, Array, Date

print(Math.random())

numbers = Array(1, 2, 3)

print(numbers.splice(1))
from domonic.javascript import URL

url = URL("https://example.com:8000/blog/article#hello")

print(url.protocol)
print(url.host)
print(url.port)
print(url.pathname)
print(url.hash)

Timers are there too:

from domonic.javascript import setTimeout

def hello():
    print("hello")

setTimeout(hello, 1000)

Other APIs include things such as:

String · Number · Promise · JSON · typed arrays · timers · URL helpers · global functions

See the JavaScript documentation for the full surface.


Web APIs

The web platform is much bigger than the DOM.

domonic implements or experiments with Python versions of APIs including:

  • URL
  • URLSearchParams
  • URLPattern
  • Fetch / XHR helpers
  • Web Storage
  • Cookie Store
  • History
  • File API
  • Web Crypto
  • Web Workers
  • WebSocket
  • Server-Sent Events
  • Messaging
  • Permissions
  • Notifications
  • Performance APIs
  • Scheduler / postTask
  • Sanitizer
  • Compression streams
  • Canvas / WebGL
  • CSS font loading
  • Gamepad
  • Media APIs
  • Import maps
  • Speculation rules
  • Custom elements
  • Shadow DOM
  • Mutation / tree observation
  • XPath

…and more.

The README deliberately doesn't try to document all of them.

👉 Browse the Web APIs


SVG, XML, MathML and more

The DOM isn't only HTML.

domonic can build other document types using the same object-oriented approach.

SVG

from domonic.html import *
from domonic.svg import *

icon = svg(
    circle(
        _cx="50",
        _cy="50",
        _r="40",
        _stroke="green",
        _fill="yellow"
    ),
    _width="100",
    _height="100"
)

print(icon)

There is also support for XML, MathML, RSS, Atom and ODF, sitemaps, A-Frame and X3D, and custom elements.


Style elements from Python

DOM-style property access works too.

from domonic.html import *

box = div("hello", _id="message")

box.style.backgroundColor = "black"
box.style.fontSize = "12px"

print(box)
<div id="message" style="background-color: black; font-size: 12px;">hello</div>

dQuery

Yes, there is also a jQuery-inspired API.

Because apparently implementing the DOM wasn't enough.

from domonic.html import *
from domonic.dQuery import º

page = html(
    body(
        li(_class="thing"),
        div(_id="test")
    )
)

print(º("#test"))
print(º(".thing"))

Append nodes:

new_div = º('<div class="child"></div>')

º("#test").append(new_div)

dQuery is useful in its own right, but it also serves as a demanding consumer of the underlying DOM implementation.

See the dQuery documentation for the full API.


d3-inspired utilities

domonic also contains a Python port / interpretation of useful parts of the d3 ecosystem built on top of its JavaScript and DOM layers.

from domonic.d3 import *

See the d3 documentation for current coverage.


BeautifulSlop

domonic includes BeautifulSlop, a BS4-style compatibility experiment built over the domonic parsing system.

It exists for code that wants familiar soup-like ergonomics while still landing in the domonic world.

See the BeautifulSlop documentation for current compatibility.


JSON utilities

Convert Python data to JSON:

from domonic.decorators import as_json

@as_json
def response():
    return {
        "hello": "world",
        "items": [1, 2, 3]
    }

print(response())

JSON arrays can also be turned into HTML tables or CSV:

import domonic.JSON as JSON

data = [{"id": "01", "name": "some item"}]

table = JSON.tablify(data)

JSON.csvify(data, "data.csv")

And CSV can go the other way:

data = JSON.csv2json("data.csv")

Animation / tweening

There is a small tweening library too.

from domonic.lerpy.easing import *
from domonic.lerpy.tween import *

position = {
    "x": 0,
    "y": 0,
    "z": 0
}

tween = Tween(
    position,
    {"x": 10, "y": 5, "z": 3},
    6,
    Linear.easeIn
)

tween.start()

Terminal APIs

domonic even contains Python wrappers around common command-line tools on Unix-like systems:

from domonic.terminal import *

print(ls())
print(pwd())
print(git("status"))
print(df())

Or run an arbitrary command:

from domonic.terminal import command

command.run("echo hello")

Windows users can use domonic.cmd. See the terminal documentation for more.


Command line

domonic comes with a CLI for working with HTML without writing a script.

Install it as a standalone command with pipx:

brew install pipx
pipx ensurepath
pipx install domonic
domonic -x https://example.com '//title'

Help

domonic -h

Version

domonic -v

Query a URL with CSS

domonic -q https://example.com 'a'
domonic -q https://example.com 'a' --parser selectolax

Query a URL with XPath

domonic -x https://example.com '//a'
domonic -x https://example.com '//a' --parser selectolax

Extract text

domonic -q https://example.com 'h1' --text

Extract attributes

domonic -q https://example.com 'a' --attr href

First result

domonic -q https://example.com 'a' --first

Count results

domonic -x https://example.com '//a' --count

Local files

domonic --xpath-file ./page.html '//title'
domonic --query-file ./page.html 'a.cta' --parser selectolax

Pipes

curl -s https://example.com | domonic -x '//a' --count
cat page.html | domonic -q 'a.cta' --attr href --parser selectolax

Evaluate pyml

domonic -e 'html(head(), body(h1("hello")))'

Scaffold a project

domonic -p myproject

Choose a server:

domonic -p myproject --server fastapi

Server-side HTML

Because domonic elements are Python objects that render to markup, they work naturally in Python web applications.

The repository contains examples for frameworks including:

  • FastAPI
  • Flask
  • Django
  • Sanic

…and others. See the servers documentation for framework-specific snippets.


Examples

There are working examples throughout the repository:

👉 github.com/byteface/domonic/tree/master/examples

Some projects built using domonic:

Extends domonic to have even further capabilities!

A JavaScript interpreter in pure Python!

A browser-based file OS and an example of building components with domonic.

A cron viewer.

A small game.

A lightweight, low-dependency DOM-focused relative of domonic.


Documentation

Use the docs for detailed API coverage, package-specific examples and less common functionality.

Useful links:


Development

Clone the repository and install the development dependencies:

python3 -m pip install -r requirements-dev.txt

Run the test suite:

make test

Or:

pytest tests

Run an individual module:

python -m unittest tests.test_html

Coverage:

coverage run -m unittest discover tests/
coverage report

The tests are also useful as executable examples of the API.


Contributing

Contributions are welcome.

  1. Fork the repository
  2. Create a branch
  3. Make your change
  4. Add or update tests where appropriate
  5. Open a pull request

See CONTRIBUTING.md for more information.


⭐ If you find it useful, consider starring the project.

Documentation · PyPI · Examples · Releases

About

Python DOM and HTML toolkit - generate, parse and manipulate HTML/XML/SVG with CSS selectors, XPath, Web APIs and JavaScript-like utilities.

Topics

Resources

Contributing

Stars

144 stars

Watchers

5 watching

Forks

Releases

Packages

Used by

Contributors

Languages