cssselect2

CSS4 selector matching for Python ElementTree-like document trees

Library
PyPI
v0.9.0
35stars
BSD 3-Clause License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
30/100Needs Attention
Development Activity4
Maintenance20
Community24
Maturity60
Momentum12

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
70/100Good
Architecture70
Code Quality74
Innovation60
Learning Curve75

cssselect2 is a focused Python library that implements CSS4 Selectors against markup documents parsed by ElementTree-like APIs — including the standard library’s ElementTree, lxml, cElementTree, and html5lib. Given a parsed document tree and a CSS selector string, it compiles the selector and matches it against elements, letting Python code apply CSS-style targeting without a browser or full CSS engine.

The library is deliberately small in scope: it depends only on tinycss2 (for CSS tokenizing) and webencodings, and its entire public surface fits in a handful of modules (parser.py, compiler.py, tree.py). It’s best known as an internal building block of WeasyPrint, the Python HTML/CSS-to-PDF renderer, where it resolves which CSS rules apply to which elements in a document.

What You Get

  • CSS4 Selector parsing and compilation against ElementTree-like document trees
  • A tree wrapper (tree.py) that adapts ElementTree/lxml/html5lib-style trees into a matchable structure
  • Selector-to-element matching for arbitrary XML/HTML documents without a browser dependency
  • Minimal dependency footprint — only tinycss2 and webencodings
  • Pure-Python implementation that works identically on CPython and PyPy

Common Use Cases

  • Determining which CSS rules apply to which elements when building a custom CSS-to-X renderer (as WeasyPrint does for PDF generation)
  • Filtering or extracting elements from parsed HTML/XML documents using CSS selector syntax instead of XPath
  • Building document-processing or static-site tooling that needs CSS selector matching without a full browser engine
  • Implementing scraping or transformation tools that select nodes by CSS selector on lxml/ElementTree trees

Under The Hood

Architecture — The library is split into three small modules: parser.py parses selector token streams (built on tinycss2) into an AST, compiler.py compiles that AST into matcher objects that can be evaluated against a tree, and tree.py wraps the input ElementTree/lxml-style tree into a uniform interface the compiler’s matchers can walk (handling namespaces, pseudo-classes, and tree traversal). This mirrors the classic parse → compile → match pipeline used by similar selector engines like the original cssselect. Tech Stack — Pure Python 3.10+, packaged with flit_core. Only two runtime dependencies: tinycss2 for CSS tokenizing and webencodings for encoding detection — both maintained by the same CourtBouillon/Kozea team, giving the library a tightly coupled, cohesive dependency chain rather than a sprawling one. Code Quality — Tests live in tests/test_cssselect2.py alongside fixtures (content.xhtml, shakespeare.html, ids.html) and a w3_selectors.py module that appears to draw on W3C selector test data, plus a make_selectors.py generator script — indicating the test suite is built to validate against the CSS selectors specification rather than just ad hoc examples. CI is configured via .github/workflows/tests.yml. API Design — The public API is intentionally minimal: parse a selector, compile it, match it against a tree, mirroring how a developer would already think about CSS selector matching. Documentation is hosted externally at doc.courtbouillon.org rather than in the repo, and the README is concise, pointing to the docs, changelog, and professional support channel rather than duplicating usage examples inline.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search