D4Vinci/Scrapling · error · ExpressionError

The functional pseudo-element ::{pseudo_element.name}() is u

Error message

The functional pseudo-element ::{pseudo_element.name}() is unknown

What it means

Raised by `xpath_pseudo_element` in Scrapling's translator (scrapling/core/translator.py:99). When translating a CSS selector, each pseudo-element is dispatched to a handler method named `xpath_<name>_functional_pseudo_element`. Scrapling implements `::attr()`; any other functional pseudo-element (one written with parentheses, e.g. `::nth-child(2)` misused as pseudo-element, `::lang(en)`, `::dir(rtl)`) has no handler, so an `ExpressionError` (cssselect's exception, exported for compatibility) is raised.

Source

Thrown at scrapling/core/translator.py:99

    """This mixin adds support to CSS pseudo elements via dynamic dispatch.

    Currently supported pseudo-elements are ``::text`` and ``::attr(ATTR_NAME)``.
    """

    def xpath_element(self: TranslatorProtocol, selector: Element) -> XPathExpr:
        # https://github.com/python/mypy/issues/14757
        xpath = super().xpath_element(selector)  # type: ignore[safe-super]
        return XPathExpr.from_xpath(xpath)

    def xpath_pseudo_element(self, xpath: OriginalXPathExpr, pseudo_element: PseudoElement) -> OriginalXPathExpr:
        """
        Dispatch method that transforms XPath to support the pseudo-element.
        """
        if isinstance(pseudo_element, FunctionalPseudoElement):
            method_name = f"xpath_{pseudo_element.name.replace('-', '_')}_functional_pseudo_element"
            method = getattr(self, method_name, None)
            if not method:  # pragma: no cover
                raise ExpressionError(f"The functional pseudo-element ::{pseudo_element.name}() is unknown")
            xpath = method(xpath, pseudo_element)
        else:
            method_name = f"xpath_{pseudo_element.replace('-', '_')}_simple_pseudo_element"
            method = getattr(self, method_name, None)
            if not method:  # pragma: no cover
                raise ExpressionError(f"The pseudo-element ::{pseudo_element} is unknown")
            xpath = method(xpath)
        return xpath

    @staticmethod
    def xpath_attr_functional_pseudo_element(xpath: OriginalXPathExpr, function: FunctionalPseudoElement) -> XPathExpr:
        """Support selecting attribute values using ::attr() pseudo-element"""
        if function.argument_types() not in (["STRING"], ["IDENT"]):  # pragma: no cover
            raise ExpressionError(f"Expected a single string or ident for ::attr(), got {function.arguments!r}")
        return XPathExpr.from_xpath(xpath, attribute=function.arguments[0].value)

    @staticmethod
    def xpath_text_simple_pseudo_element(xpath: OriginalXPathExpr) -> XPathExpr:

View on GitHub (pinned to 5d213a2d47)

Solutions

  1. Restrict pseudo-elements in Scrapling selectors to the supported set: `::text`, `::attr(name)` (and standard structural pseudo-classes like `:nth-child()` written with one colon, which cssselect handles natively).
  2. Replace unsupported functional pseudo-elements with equivalent XPath: `p::lang(en)` -> select then filter by `element.lang` attribute in Python.
  3. If you need browser-grade CSS, evaluate the selector in the browser engine via the fetcher's `page_action`/JS instead of the translator.
  4. Check the exact selector string for typos before assuming a missing feature.

Example fix

# before
links = page.css('a::attr(href)')
lang_paras = page.css('p::lang(en)')  # ExpressionError

# after
links = page.css('a::attr(href)')
lang_paras = [p for p in page.css('p') if p.attrib.get('lang', '').startswith('en')]
Defensive patterns

Strategy: validation

Validate before calling

import re

SUPPORTED_FUNCTIONAL = {'attr'}

def selector_uses_supported_pseudo_elements(css: str) -> bool:
    found = re.findall(r'::([a-zA-Z-]+)\(', css)
    return all(name in SUPPORTED_FUNCTIONAL for name in found)

Try / catch

from cssselect.parser import ExpressionError  # same class scrapling re-raises
try:
    vals = page.css('p::lang(en)')
except Exception as e:
    if 'pseudo-element' in str(e):
        vals = [p for p in page.css('p') if p.attrib.get('lang', '').startswith('en')]
    else:
        raise

Prevention

When it happens

Trigger: Calling `selector.css('p::lang(en)')`, `selector.css('div::dir(rtl)')`, or any `::<name>(...)` pseudo-element other than `::attr()` in Scrapling. Anything with parentheses that isn't `::attr(...)` hits this branch.

Common situations: Copy-pasting selectors written for Scrapy (which supports a similar set) or from browser DevTools; assuming all CSS pseudo-elements work because `::text` and `::attr()` do; typos like `::atrr(href)`.

Related errors


AI-assisted analysis of D4Vinci/Scrapling@5d213a2d47 (2026-08-14). Data as JSON: /api/errors/07097aef898e8717. Report an issue: GitHub.