Soup Sieve

repository·main·Indexed 42 days ago

https://github.com/facelessuser/soupsieve

A modern CSS selector implementation for Beautiful Soup 4 that provides advanced selecting, matching, and filtering capabilities using CSS specifications up to CSS level 4. It supports classes, IDs, attributes, combinators, and pseudo-classes like :not(), :is(), and :has(). The library can be used via Beautiful Soup's .select() method or directly through its own API, including functions such as select(), select_one(), iselect(), closest(), match(), and filter().

Tokens
25.9K
Snippets
118
Records
138
Agent score
54%

What's inside Soup Sieve

  1. Overview of Soup Sieve CSS Selectors

    main

    Soup Sieve is a CSS selector library for Beautiful Soup 4 that enables selecting, matching, and filtering using modern CSS selectors (from CSS level 1 through the latest CSS level 4 drafts).

    It can be used automatically by Beautiful Soup's .select() method (as of version 4.7.0) or imported directly for specialized parsing.

    Supported selector types include:

    • Classes: .classes
    • IDs: #ids
    • Attributes: [attributes=value]
    • Combinators: parent child, parent > child, sibling ~ sibling, sibling + sibling
    • Pseudo-classes: :not(), :is(), :has()

    Note: Selectors that do not have meaningful functionality in a non-browser environment will simply not match anything.

  2. Advanced Selector Syntax

    main

    Soup Sieve supports modern CSS selectors from Level 1 through Level 4 drafts. Selectors can be multi-line and include CSS-style comments for documentation.

    Supported Selector Types

    • Classes: .class
    • IDs: #id
    • Attributes: [attr=value]
    • Combinators: parent child, parent > child, sibling ~ sibling, sibling + sibling
    • Pseudo-classes: :not(), :is(), :has()

    Multi-line and Commented Selectors

    You can define complex selectors as multi-line strings and include comments within them:

    selector = """
    /* This is a comment */
    .a,
    /* This is another comment */
    .b
    """
    sv.select(selector, soup)
  3. Use the Child Combinator with `>`

    main

    The child combinator uses the > symbol. It matches an element only if it is a direct child of the first selector (i.e., the first selector is its immediate parent).

    >>> from bs4 import BeautifulSoup as bs
    >>> html = """
    ... <html>
    ... <head></head>
    ... <body>
    ... <div><p>Paragraph 1</p></div>
    ... <div><ul><li><p>Paragraph 2</p></li></ul></div>
    ... </body>
    ... </html>
    ... """
    >>> soup = bs(html, 'html5lib')
    >>> print(soup.select('div > p'))
    [<p>Paragraph 1</p>]
  4. Understand the Compiled CSS Selector Structure

    main

    When a CSS selector string is parsed by CSSParser, it returns a SelectorList. This object represents the hierarchy of the compiled selector.

    Mental Model

    • SelectorList: A collection of one or more Selector objects. A comma-separated selector like div, p results in a SelectorList containing two Selector objects.
    • Selector: Represents a single compound selector. A compound selector is a single unit that may chain other SelectorList objects through pseudo-classes (like :is(), :not(), or :has()) or through relational selectors (like > or +).
    • Chaining: Complex selectors are built by nesting. For example, in div > p, the Selector for p contains a relation attribute that points to a SelectorList containing the Selector for div.

    To debug the compiled structure, you can access the .selectors attribute on a compiled object and use the .pretty() method.

    >>> import soupsieve as sv
    >>> sv.compile('this > that.class[name=value]').selectors.pretty()
    SelectorList(
        selectors=(
            Selector(
                tag=SelectorTag(name='that', prefix=None),
                # ... (rest of the nested structure)
            ),
        ),
        is_not=False,
        is_html=False)
  5. Use ID Selectors to match specific IDs

    main

    The ID selector (#id) matches an element based on its id attribute. The ID must be an exact match. While primarily an HTML concept, this is also supported for XML.

    >>> from bs4 import BeautifulSoup as bs
    >>> html = """
    ... <html>
    ... <head></head>
    ... <body>
    ...    <div id="some-id">Here is some text.</div>
    ...    <div>Here is some more text.</div>
    ... </body>
    ... </html>
    ... """
    >>> soup = bs(html, 'html5lib')
    >>> print(soup.select('#some-id'))
    [<div id="some-id">Here is some text.</div>]
  6. Use Namespace Selectors

    main

    Namespace selectors allow you to target elements or attributes within specific XML/HTML namespaces. They are used with type selectors, universal selectors, or attribute names using the syntax namespace|selector.

    Key Rules:

    • Prefix Mapping: The prefix used in the selector (e.g., svg) does not need to match the prefix in the document; Soup Sieve compares the associated URI defined in the namespaces dictionary.
    • Universal Selector: The * character can represent any namespace (e.g., *|element or *|*).
    • Default Namespaces: If a default namespace is defined (using an empty key "": "URI"), type selectors will assume that namespace unless an explicit namespace is provided.
    • Omitted Namespaces: Using |element matches any element that has no namespace.
    • Attribute Selectors: Namespaces can be used in attribute selectors (e.g., [ns|attr]). However, [|attr] is equivalent to [attr] (matching attributes regardless of namespace).
    >>> from bs4 import BeautifulSoup as bs
    >>> html = """
    ... <html>
    ... <body
    ... <h1>SVG Example</h1>
    ... <p><a href="http://facelessuser.github.io/soupsieve/">Soup Sieve Docs</a></p>
    ... <svg viewBox="0 0 160 40" xmlns="http://www.w3.org/2000/svg">
    ...   <a xlink:href="https://developer.mozilla.org/"><text x="10" y="25">MDN Web Docs</text></a>
    ... </svg>
    ... </body>
    ... </html>
    ... """
    >>> soup = bs(html, 'html5lib')
    >>> # Match an 'a' tag specifically in the SVG namespace
    >>> print(soup.select('svg|a', namespaces={'svg': 'http://www.w3.org/2000/svg'}))
    [<a xlink:href="https://developer.mozilla.org/"><text x="10" y="25">MDN Web Docs</text></a>]
    
    >>> # Match an attribute with a namespace
    >>> print(soup.select('[xlink|href]', namespaces={'xlink': 'http://www.w3.org/1999/xlink'}))
    [<a xlink:href="https://developer.mozilla.org/"><text x="10" y="25">MDN Web Docs</text></a>]
  7. Use Selector Lists with commas

    main

    You can use a comma (,) to create a selector list. When using a selector list, any element that matches any of the individual selectors in the list will be returned.

    >>> from bs4 import BeautifulSoup as bs
    >>> html = """
    ... <html>
    ... <head></head>
    ... <body>
    ... <h1>Title</h1>
    ... <p>Paragraph</p>
    ... </body>
    ... </html>
    ... """
    >>> soup = bs(html, 'html5lib')
    >>> print(soup.select('h1, p'))
    [<h1>Title</h1>, <p>Paragraph</p>]
  8. Understand selector terminology

    main

    Soup Sieve uses standard CSS terminology to categorize selectors:

    • Selector: A general term covering simple, compound, or complex selectors.
    • Simple Selector: Represents a single condition on an element. Examples include:
      • Type selector
      • Universal selector
      • ID selector
      • Class selector
      • Attribute selector
      • Pseudo-class selector
    • Compound Selector: A sequence of simple selectors without combinators. If a universal or type selector is used, it must appear first, and you cannot use both a universal and a type selector in the same compound selector.
    • Complex Selector: Multiple simple or compound selectors joined together using combinators.
    • Selector List: A list of selectors separated by commas (,). A match is valid if any selector in the list matches the element.
  9. Understand non-applicable pseudo-classes in Soup Sieve

    main

    Soup Sieve recognizes several CSS pseudo-classes that are considered "non-applicable" in a Beautiful Soup environment. While the parser will correctly recognize and parse these selectors without error, they will never match any elements.

    This is because these pseudo-classes rely on a live, interactive browser environment (such as user interaction, media playback state, or focus state) which does not exist when parsing static HTML/XML documents. If these pseudo-classes are removed from the CSS specification in the future, Soup Sieve will simply remove them from its supported set.

  10. Understand Soup Sieve document type behavior

    main

    Soup Sieve adjusts its behavior based on the document type detected from the Beautiful Soup object:

    • HTML (HTML, HTML5): Tag names and attribute names are treated case-insensitively.
    • XML (including XHTML): Tag names and attribute names are treated case-sensitively.
    • Namespaces: Evaluated based on the parser's support.

    Namespace Support Tips

    • For HTML5 namespace support, use the html5lib parser. The lxml HTML parser does not provide proper namespaces.
    • For XML/XHTML, use the lxml-xml parser (often referred to as xml) to ensure proper namespace handling.

    Attribute Case Sensitivity

    While attribute values are generally case-sensitive, the type attribute in HTML/HTML5 is always treated as case-insensitive. To force case-sensitivity for the type attribute, use the s flag in your selector: #!css [type="submit" s].

  11. Use Class Selectors to match elements by class

    main

    The class selector (.class) matches elements based on the values in their class attribute. The class attribute is treated as a whitespace-separated list of individual classes. This is also supported for XML.

    >>> from bs4 import BeautifulSoup as bs
    >>> html = """
    ... <html>
    ... <head></head>
    ... <body>
    ...    <div class="some-class">Here is some text.</div>
    ...    <div>Here is some more text.</div>
    ... </body>
    ... </html>
    ... """
    >>> soup = bs(html, 'html5lib')
    >>> print(soup.select('.some-class'))
    [<div class="some-class">Here is some text.</div>]
  12. Use Universal Selectors to match any element

    main

    The Universal selector (*) matches every element in the document regardless of its type.

    >>> from bs4 import BeautifulSoup as bs
    >>> html = """
    ... <html>
    ... <head></head>
    ... <body>
    ...    <p>Here is some text.</p>
    ...    <div>Here is some more text.</div>
    ... </body>
    ... </html>
    ... """
    >>> soup = bs(html, 'html5lib')
    >>> print(soup.select('*'))
    [<html><head></head>... <head></head>... <body...>... <div...>...]