Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use this XPath 1.0 expression to select every element whose whitespace-separated class attribute contains the token notice:

//*[contains(concat(' ', normalize-space(@class), ' '), ' notice ')]

Replace notice with your class name. The padding and whitespace normalization make the test match notice when it appears alongside other classes, while avoiding false matches such as noticeable. XPath only queries a document or DOM supplied by your parser, scraper, or browser automation tool; it does not fetch or render the page itself.

The reliable class-token XPath

HTML usually stores several classes in one attribute. For example, <div class="notice highlighted"> has two class tokens. Comparing the complete attribute with @class='notice' misses that element. A bare substring test such as contains(@class, 'notice') can instead match an unrelated token such as noticeable.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The dependable XPath 1.0 form is:

//*[contains(concat(' ', normalize-space(@class), ' '), ' notice ')]

  1. normalize-space(@class) trims leading and trailing whitespace and collapses runs of whitespace.
  2. concat(' ', ..., ' ') adds a boundary space at both ends.
  3. The search string, ' notice ', includes spaces, so the comparison is token-boundary-aware.

It matches class="notice", class="notice highlighted", and class="highlighted notice", but not class="noticeable". Keep the class name inside the quoted string and quote or escape it correctly when your programming language builds the XPath.

Choose the element scope

Search every element type

Use //* when the class may occur on any element:

//*[contains(concat(' ', normalize-space(@class), ' '), ' notice ')]

Restrict the tag name

Use a tag test when you know the expected element type:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

//div[contains(concat(' ', normalize-space(@class), ' '), ' notice ')]

This reduces the result set and documents that only div elements are valid matches. You can substitute section, button, li, or another element name.

Query descendants of a current node

When your API has already selected a container and you want matches below that node, use a relative expression beginning with a dot:

.//*[contains(concat(' ', normalize-space(@class), ' '), ' notice ')]

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The leading dot matters. Without it, // is evaluated from the document root in many XPath APIs rather than from the current context node. Parsel’s documentation specifically demonstrates CSS selection followed by relative XPath using ..

Require more than one class

Add one token predicate for each required class. This selects elements that have both notice and urgent:

Rank #2
XPath 2.0 Programmer's Reference
  • Used Book in Good Condition

//*[contains(concat(' ', normalize-space(@class), ' '), ' notice ') and contains(concat(' ', normalize-space(@class), ' '), ' urgent ')]

Additional predicates are joined with and. The order of classes in the HTML does not matter. This is equivalent to a compound CSS selector such as .notice.urgent when CSS is available.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

When CSS is a better selector

If your task is only class membership and the library supports CSS selectors, .notice is shorter and usually easier to read. The W3C Selectors specification defines HTML, SVG, and MathML class membership in terms of whitespace-separated class tokens. Parsel recommends CSS for ordinary class lookup and XPath when you need XPath-specific navigation or predicates.

Need CSS XPath
Any element with one class .notice //*[contains(concat(' ', normalize-space(@class), ' '), ' notice ')]
Two classes on one element .notice.urgent Two token predicates joined with and
Text, parent, sibling, or positional predicates Limited or API-dependent XPath is usually more expressive
Relative query after selecting a container API-dependent Use .//*[...] or .//tag[...]

Choose based on the selector job, API support, and whether the query is document-wide or relative to a selected node. Do not switch to a substring test merely to shorten the expression.

Useful XPath patterns

Select a class on a specific element

//button[contains(concat(' ', normalize-space(@class), ' '), ' primary ')]

This finds buttons carrying the primary token, including buttons with additional classes.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Combine class and text conditions

XPath lets you add other predicates to the same node test. For example:

//div[contains(concat(' ', normalize-space(@class), ' '), ' notice ') and contains(normalize-space(.), 'Warning')]

The class test remains token-safe while the second predicate checks normalized descendant text. Adjust text matching for your document’s case and language requirements.

Apply a query inside a selected container

After selecting a product card, panel, or other context node, query its descendants:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

.//span[contains(concat(' ', normalize-space(@class), ' '), ' price ')]

Use .// for descendants of the context node. Use a direct-child path such as ./div[...] when nesting must be limited.

Get the first result correctly

Position predicates have an important scope distinction:

  • //li[1] selects li nodes that are first among their respective parents.
  • (//li)[1] selects only the first li in the document-wide result.

The same rule applies to a class-filtered path. To get the first matching notice overall, parenthesize the complete expression:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

(//*[contains(concat(' ', normalize-space(@class), ' '), ' notice ')])[1]

Using the expression in common tools

Parsel and Scrapy

Parsel and Scrapy expose XPath selectors on a response or selected node. A document-wide query can look like:

response.xpath("//*[contains(concat(' ', normalize-space(@class), ' '), ' notice ')]")

For a CSS-to-XPath chain, first select a container and then use a relative XPath:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

response.css('article.card').xpath(".//*[contains(concat(' ', normalize-space(@class), ' '), ' notice ')]")

Using // instead of .// in the second call can escape the selected container and return matches elsewhere in the document.

Selenium WebDriver

Selenium’s locator API accepts an XPath string. In Python:

from selenium.webdriver.common.by import By
elements = driver.find_elements(By.XPATH, "//*[contains(concat(' ', normalize-space(@class), ' '), ' notice ')]")

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

For one element, use find_element; for a collection, use find_elements. Selenium locators operate on the browser’s current DOM, so wait for client-rendered content before querying when necessary. The locator syntax is documented in Selenium’s element-locator guide.

Python lxml-style parsers

Libraries that expose XPath generally accept the same expression:

matches = tree.xpath("//*[contains(concat(' ', normalize-space(@class), ' '), ' notice ')]")

Always check the library’s return type: some APIs return element objects, while others return strings when the XPath ends in /text() or an attribute expression.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Dynamic class values, namespaces, and quoting

Dynamic or generated classes

Frameworks may add classes at runtime, reorder them, or generate names that change between builds. The token expression tolerates order and additional classes, but it cannot identify a class that is absent from the DOM at query time. Inspect the rendered DOM in browser developer tools or wait for the relevant state before selecting.

XML and namespaces

In XML, an unprefixed name test such as //div does not automatically match elements in a namespace. Register the document’s namespace prefix with your XPath API and use that prefix, or use a namespace-aware strategy supported by the host library. The class-token predicate itself still applies to the class attribute when that attribute is unqualified.

Safely insert a variable class name

Do not concatenate untrusted text into an XPath without escaping quotes. XPath 1.0 string literals use either single or double quotes. If a class value can contain both quote characters, construct a concat() literal in the host language; otherwise choose the opposite quote style. Typical HTML class tokens do not contain quotes, but generated selectors and user input should still be validated.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshooting checklist

No matches returned

  • Inspect the actual class attribute; the value may be on a parent, a shadow DOM component, or a different rendered state.
  • Confirm that the host API is querying the current document rather than an old response or a different frame.
  • Check relative context: use .// after selecting a node.
  • Verify namespaces when parsing XML or SVG.
  • Wait for JavaScript-rendered content in browser automation.

Too many matches

  • Replace //*[...] with a specific tag such as //div[...] when appropriate.
  • Add another class predicate, an attribute condition, text, or an ancestor constraint.
  • Check for duplicated components, templates, or hidden nodes in the DOM.

False matches such as “noticeable”

Replace contains(@class, 'notice') with the padded normalize-space() expression. The spaces around the search token are what enforce class boundaries.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The first-item query returns several nodes

Use parentheses around the full result before [1]: (//...)[1]. Without parentheses, the positional predicate is applied within each matching parent context.

Elements appear visually but are not selectable

A screenshot, canvas drawing, or closed shadow root is not necessarily represented as ordinary descendant elements in the queried DOM. XPath can select nodes exposed to the host parser or browser context; it cannot inspect pixels. If you need a rendered capture for debugging, use a screenshot service after confirming that the selector problem is separate from the DOM query.

Or skip the browser setup

If your goal is to obtain a clean page image while debugging selectors, ScreenshotNeo provides a single HTTP request. Its cleanup steps accept cookie or consent banners and remove more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the page verdict and billing status in X-Page-Verdict and X-Billed headers. It also offers an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.

Example cURL request (see the ScreenshotNeo API documentation):

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python:

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; the service also supports full-page and element captures, custom CSS and JavaScript, waits, blocking rules, device presets, PDFs, caching, signed links, asynchronous jobs, bulk capture, and a usage API. Sign up free for ScreenshotNeo.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Frequently Asked Questions

Does XPath have a dedicated class() function?

No. XPath 1.0 has no CSS-style class-membership function, so use the token-boundary expression with contains(), concat(), and normalize-space().

Can I use this pattern with an element that has no class attribute?

Yes. An absent @class becomes an empty string in this predicate and does not match the requested token.

Should I use CSS or XPath for a simple class lookup?

Use CSS when your API supports it and you only need class membership; use XPath when you also need text, ancestry, sibling, or positional predicates.

Why does my XPath work in the full page but not inside a selected node?

A leading // commonly starts a document-wide search. Use a relative expression beginning with .// when querying descendants of the current node.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.