cssselect: CSS Selectors for Python¶
This module used to live inside of lxml as
lxml.cssselect before it was
extracted as a stand-alone project.
- Free software: BSD licensed
- Compatible with Python 2.6+ and 3.3+
- Latest documentation on Read the Docs
- Source, issues and pull requests on Github
- Releases on PyPI
- Install with
pip install cssselect
HTMLTranslator for HTML documents,
for “generic” XML documents. (The former has a more useful translation
for some selectors, based on HTML-specific element types or attributes.)
>>> from cssselect import GenericTranslator, SelectorError >>> try: ... expression = GenericTranslator().css_to_xpath('div.content') ... except SelectorError: ... print('Invalid selector.') ... >>> print(expression) descendant-or-self::div[@class and contains(concat(' ', normalize-space(@class), ' '), ' content ')]
The resulting expression can be used with lxml’s XPath engine:
>>> from lxml.etree import fromstring >>> document = fromstring(''' ... <div id="outer"> ... <div id="inner" class="content body">text</div> ... </div> ... ''') >>> [e.get('id') for e in document.xpath(expression)] ['inner']
In CSS3 Selectors terms, the top-level object is a group of selectors, a
sequence of comma-separated selectors. For example,
div, h1.title + p
is a group of two selectors.
This library implements CSS3 selectors as described in the W3C specification. In this context however, there is no interactivity or history of visited links. Therefore, these pseudo-classes are accepted but never match anything:
Additionally, these depend on document knowledge and only have a useful
they never match:
These applicable pseudo-classes are not yet implemented:
*:only-of-type. All of these work when you specify an element type, but not with
On the other hand, cssselect supports some selectors that are not in the Level 3 specification:
:contains(text)pseudo-class that existed in an early draft but was then removed.
[foo!=bar]is the same as
:not()accepts a sequence of simple selectors, not just single simple selector. For example,
:not(a.important[rel])is allowed, even though the negation contains 3 simple selectors.
HTMLTranslator is a subclass of
you can make new sub-classes of either of them and override some methods.
This enables you, for example, to customize how some pseudo-class is
implemented without forking or monkey-patching cssselect.
The “customization API” is the set of methods in translation classes and their signature. You can look at the source code to see how it works. However, be aware that this API is not very stable yet. It might change and break your sub-class.
In CSS you can use
namespace-prefix|element, similar to
namespace-prefix:element in an XPath expression. In fact, it maps
one-to-one. How prefixes are mapped to namespace URIs depends on the
Released on 2017-01-10.
- Add support for Python 3.6.
- Documentation hosted on Read the Docs
Released on 2016-10-21.
- Add code coverage reports.
:nth-*(an+b)pseudo-classes selectors. (except
*:nth-child()which looks untranslatable to XPath 1.0.)
Released on 2016-06-15.
- Distribute as universal wheel.
- Add support for Python 3.3, 3.4 and 3.5.
- Drop support for Python 2.5 as testing is getting difficult.
- Improve tests on pseudo-elements.
Released on 2013-10-17.
- Backward incompatible change from 0.9:
selector_to_xpath()defaults to ignoring pseudo-elements, as it did in 0.8 and previous versions. (
- Drop official support for Python 2.4 and 3.1, as testing was becoming difficult. Nothing will break overnight, but future releases may on may not work on these versions. Older releases will remain available on PyPI.
Released on 2013-10-11.
Add parser support for
This version accidentally introduced a backward incompatible change:
selector_to_xpath() defaults to
rejecting pseudo-elements instead of ignoring them.
Released on 2013-03-15.
- #22 Let extended translators override what XPathExpr class is used
Use the built-in
lang()XPath function for implementing the
:lang()pseudo-class with XML documents. This is probably faster than
Released on 2012-06-14. Code name remember-to-test-with-tox.
0.7 broke the parser in Python 2.4 and 2.5; the tests in 2.x. Now all is well again.
Also, pseudo-elements are now correctly made lower-case. (They are supposed to be case-insensitive.)
Released on 2012-06-14.
Bug fix release: see #2, #7 and #10 on GitHub.
- The tokenizer and parser have been rewritten to be much closer to the specified grammar. In particular, non-ASCII characters and backslash-escapes are now handled correctly.
- Special characters are protected in the output so that generated XPath exrpessions should always be valid
*=attribute operators now correctly never match when used with an empty string.
Released on 2012-04-25.
Make sure that internal token objects do not “leak” into the public API and
Selector.pseudo_element is an unicode string.
Released on 2012-04-24.
setup.pyuse setuptools/distribute if available, but fall back on distutils.
- Implement the
:lang()pseudo-class, although it is only based on
langattributes. If the document language is known from some other meta-data (like a
Content-LanguageHTTP header or
<meta>element), a workaround is to set a lang attribute on the root element.
Released on 2012-04-20.
- Fix case sensitivity issues.
HTMLTranslatorbased on the HTML5 specification rather than guessing; add the
- Several bug fixes and better test coverage.
Released on 2012-04-18.
- Add proper support for pseudo-elements
- Add specificity calculation
- Expose the
parse()function and the parsed
Selectorobjects in the API.
- Add the
Released on 2012-04-17.
- Fix many parsing bugs.
- Rename the
- There, implement
:visitedas never matching.
- Make a new HTML-specific
HTMLTranslatorsubclass. There, implement
:visitedas appropriate for HTML, with all links “not visited”.
- Remove the
css_to_xpath()function. The translator classes are the new API.
- Add support for
:contains()back, but case-sensitive. lxml will override it to be case-insensitive for backward-compatibility.
Discussion is open if anyone is interested in implementing eg.
:visited differently, but they can always do it in a
Released on 2012-04-16.
- Remove the
css_to_xpath()function is now the main API.)
- Remove support for the
These changes allow cssselect to be used without lxml. (Hey, this was the whole point of this project.) The tests still require lxml, though. The removed parts are expected to stay in lxml for backward-compatibility.
:contains() only existed in an early draft
of the Selectors specification, and was removed before Level 3 stabilized.
Internally, it used a custom XPath extension function which can be
difficult to express outside of lxml.
- Separate the XPath translation from the parsed objects into a new
Translator can be made to change the way that some selector
(eg. a pseudo-class) is implemented.
Released on 2012-04-13.
Extract lxml.cssselect from the rest of lxml and make it a stand-alone project.
ea53ceaf7e44ba4fbb5c818ae31370932f47774e was taken on 2012-04-11
from the ‘master’ branch of lxml’s git repository. This is somewhere
between versions 2.3.4 and 2.4.
The commit history has been rewritten to:
- Remove lxml files unrelated to cssselect
- Import the early history from the ‘html’ branch in the old SVN repository
- Fix author names in commits from SVN
This project has its own import name, tests and documentation. But the code itself is unchanged and still depends on lxml.