cssselect

repository·master·Indexed 43 days ago

https://github.com/scrapy/cssselect

A Python library that parses CSS3 selectors and translates them into XPath 1.0 expressions. It provides GenericTranslator for XML and HTMLTranslator for HTML documents, enabling CSS-style querying in environments that rely on XPath engines like lxml.

Tokens
4.3K
Snippets
12
Records
24
Agent score
52%

What's inside cssselect

  1. What is cssselect and how does it work?

    master

    cssselect is a Python library designed to bridge the gap between CSS selectors and XPath.

    Core Functionality: It takes CSS3 selectors as input and translates them into XPath 1.0 expressions.

    Workflow:

    1. Define a CSS selector (e.g., div.content).
    2. Use cssselect to convert it to an XPath expression.
    3. Pass that XPath expression to an XPath engine (such as lxml) to extract matching elements from an XML or HTML document.
  2. How to choose between HTMLTranslator and GenericTranslator

    master

    The library provides two main translator classes:

    1. HTMLTranslator: Use this for HTML documents. It includes specialized logic for HTML-specific element types and attributes, making translations more accurate for web scraping.
    2. GenericTranslator: Use this for generic XML documents. It follows a more standard approach that does not assume HTML semantics.

    Note that certain pseudo-classes like :link, :enabled, :disabled, and :checked depend on document knowledge and will only match elements when using HTMLTranslator. In GenericTranslator, they will never match.

  3. Customize translation logic via subclassing

    master

    You can customize how CSS selectors are translated into XPath by subclassing HTMLTranslator or GenericTranslator and overriding their internal methods. This allows you to implement custom logic for specific pseudo-classes without modifying the library source.

    Warning: The customization API (the set of methods in translation classes) is not yet stable and may change in future versions.

  4. Handle CSS Namespaces

    master
    CSS supports namespaces using the namespace-prefix|element syntax. This maps one-to-one to the XPath namespace-prefix:element syntax. The mapping of prefixes to specific namespace URIs is handled by the underlying XPath implementation (e.g., lxml).
  5. Quickstart: Translate CSS selectors to XPath

    master

    To convert CSS selectors into XPath expressions, use either HTMLTranslator for HTML documents or GenericTranslator for generic XML documents. HTMLTranslator provides more specialized translations for HTML-specific elements and attributes.

    The resulting XPath expression can then be used with an XPath engine like lxml to query documents.

    from cssselect import GenericTranslator, SelectorError
    
    try:
        expression = GenericTranslator().css_to_xpath('div.content')
    except SelectorError:
        print('Invalid selector.')
    
    print(expression)
    # Output: descendant-or-self::div[@class and contains(concat(' ', normalize-space(@class), ' '), ' content ')]
  6. Install cssselect via pip

    master

    You can install the cssselect library using pip. This library allows you to parse CSS3 selectors and translate them into XPath 1.0 expressions, which can then be used with engines like lxml to query XML or HTML documents.

    pip install cssselect
  7. Understand the XPathExpr abstraction

    master

    The XPathExpr class is an internal representation of a partially constructed XPath expression. While primarily used by the translators, it defines how paths, elements, and conditions are composed.

    Core attributes:

    • path: The structural path (e.g., /div/p).
    • element: The current node test (e.g., * or div).
    • condition: The predicate logic (e.g., [@class='foo']).

    Key methods for building expressions:

    • add_condition(condition, conjunction='and'): Appends a new predicate to the current expression.
    • add_name_test(): Converts the current element into a node test (e.g., self::element).
    • join(combinator, other_xpath_expr): Joins the current expression with another using a combinator (like / or //).
  8. Understand the `Selector` pseudo-element mapping

    master

    The Selector class handles pseudo-elements via the pseudo_element attribute. The mapping depends on the input syntax:

    CSS3 syntaxOlder syntaxResulting pseudo_element
    a::beforea:before'before' (string)
    li::marker(N/A)'marker' (string)
    a::foo(2)(N/A)FunctionalPseudoElement(...)
    li:marker(N/A)None (Invalid pseudo-class)
  9. Reference: Supported CSS Selectors and Limitations

    master

    The library implements CSS3 selectors with several specific behaviors and limitations regarding pseudo-classes and Level 4 features.

    Pseudo-classes that never match

    Because there is no interactivity or link history, the following pseudo-classes are accepted but will never match anything:

    • :hover, :active, :focus, :target, :visited

    Implementation Limitations

    • Type-specific pseudo-classes: *:first-of-type, *:last-of-type, *:nth-of-type, *:nth-last-of-type, and *:only-of-type work when a specific element type is provided, but do not work with the universal selector *.
    • :is() and :where(): Arguments must be a comma-separated list of compound selectors. Combinators (e.g., a b) are not allowed inside them. :not() and :scope are also rejected inside these.
    • :has(): Takes a single argument consisting of an optional leading combinator (>, +, or ~) followed by one compound selector (type, class, or universal selectors only). It does not support IDs or selector lists.
    • :not(): Takes a single argument (a complex selector). Selector lists are not supported.

    Supported Level 4 and Non-standard Extensions

    • :scope: Supported, but only at the start of a selector.
    • Attribute flags: i (case-insensitive) is supported (e.g., [href^="HTTP" i]). s is accepted but has no effect.
    • Non-standard IDs: Supports IDs that are not valid CSS identifiers (e.g., #37signals).
    • :contains(text): Supported (an early draft extension).
    • != operator: Supported (e.g., [foo!=bar]).
  10. Handle SelectorSyntaxError during parsing

    master

    The parser raises SelectorSyntaxError when it encounters invalid CSS selector syntax. Common triggers include:

    • Unclosed strings: A string literal that does not have a matching closing quote (e.g., [attr='value).
    • Invalid strings: A string that contains invalid escape sequences.
    • Unexpected token types: When using methods like next_ident() or next_ident_or_star() and the stream provides a token that does not match the expected type (e.g., expecting an IDENT but receiving a NUMBER).
  11. Handle selector parsing errors

    master

    When parsing CSS strings, you may encounter syntax errors. You can catch specific syntax errors or use the common parent exception to handle all selector-related issues.

    • SelectorSyntaxError: Raised when a selector does not match the grammar.
    • SelectorError: The base class for SelectorSyntaxError and ExpressionError. Catching this will handle both types.
    from cssselect.parser import parse, SelectorError
    
    try:
        selectors = parse("invalid selector")
    except SelectorError:
        # This catches both SelectorSyntaxError and ExpressionError
        print("Failed to parse CSS")
  12. Handle unsupported selectors with ExpressionError

    master

    When using css_to_xpath or selector_to_xpath, the library may raise an ExpressionError (which inherits from SelectorError and RuntimeError). This occurs when:

    • A selector is unknown or unsupported (e.g., certain pseudo-classes).
    • A pseudo-element is encountered where not supported.
    • A combinator is used in a context where it is not allowed (e.g., inside :is(), :where(), or :matches()).