How to select elements by class in XPath?

You can select elements by class in XPath using contains(@class, "class-name") or @class="class-name".

contains(@class, "class-name") matches any element whose class attribute contains the text class-name. That means it also matches partial class names. The second expression only matches elements whose class attribute is exactly class-name, so it won't match elements with additional classes.

If you want to match a single class without matching partial class names, use contains(concat(" ", normalize-space(@class), " "), " class-name "). It matches class="class-name active", but it won't match class="class-name-large".

Here is a sample XPath code in Selenium that demonstrates all three approaches:

from urllib.parse import quote
from selenium import webdriver
from selenium.webdriver.common.by import By

# A local HTML snippet, so no external page is required.
# This example assumes Selenium can launch Chrome in your environment.

html = """
<html>
<body>
    <h1 class="mb-[33px]">The Best Web Scraping API</h1>
    <h1 class="mb-[33px] hero-title">Web Scraping API with multiple classes</h1>
    <h1 class="mb-[33px]-large">Web Scraping API with a similar class name</h1>
</body>
</html>
"""

# Launch a new Chrome browser instance
driver = webdriver.Chrome()

try:
    # Load the HTML directly via a data URL
    driver.get("data:text/html;charset=utf-8," + quote(html))

    # Example 1
    # contains(@class, ...) matches any element whose class attribute
    # contains "mb-[33px]", including partial class names.
    contains_matches = driver.find_elements(
        By.XPATH,
        "//h1[contains(@class, 'mb-[33px]')]"
    )
    print("contains(@class, ...):")
    for element in contains_matches:
        print("-", element.text)
    # Expected output:
    # contains(@class, ...):
    # - The Best Web Scraping API
    # - Web Scraping API with multiple classes
    # - Web Scraping API with a similar class name

    # Example 2
    # @class="..." matches only when the entire class attribute
    # is exactly "mb-[33px]".
    exact_matches = driver.find_elements(
        By.XPATH,
        "//h1[@class='mb-[33px]']"
    )
    print("\n@class='mb-[33px]':")
    for element in exact_matches:
        print("-", element.text)
    # Expected output:
    # @class='mb-[33px]':
    # - The Best Web Scraping API

    # Example 3
    # Match "mb-[33px]" as a whole class name, even when the element
    # has multiple classes. This avoids partial matches.
    whole_class_matches = driver.find_elements(
        By.XPATH,
        '//h1[contains(concat(" ", normalize-space(@class), " "), " mb-[33px] ")]'
    )
    print("\nWhole class match:")
    for element in whole_class_matches:
        print("-", element.text)
    # Expected output:
    # Whole class match:
    # - The Best Web Scraping API
    # - Web Scraping API with multiple classes

finally:
    driver.quit()

Note: The example uses different outer Python quotes ('...' and "...") to avoid escaping the quotes inside the XPath expression. Both styles are equivalent.

Related XPath web scraping questions: