What is an XPath selector?
XPath is a query language for addressing parts of a document tree. It was designed for XML and works on HTML once a parser has built the tree. Where a CSS selector describes a pattern to match, an XPath expression describes a path to walk, and it can walk in directions CSS has no way to express.
What it can do that CSS cannot
Three things, and each solves a problem that comes up constantly in scraping.
Move upward. //span[@class="price"]/../.. selects the grandparent of a matching element. When the data you want has no useful identifier of its own but sits next to something that does, this is the way to reach it. CSS has no parent selector.
Match on text. //button[text()="Add to cart"] finds an element by what it says. //div[contains(text(), "In stock")] matches partially. CSS cannot inspect text content at all, which is a hard limit when a page's only stable marker is its wording.
Use axes. following-sibling, preceding, ancestor and the rest let you express relationships CSS cannot reach. //dt[text()="SKU"]/following-sibling::dd[1] pulls the value out of a definition list by its label, which is a very common shape on specification tables.
The syntax, briefly
| Expression | Meaning |
|---|---|
//div |
every div at any depth |
//div[@class="price"] |
by attribute value |
//div[contains(@class,"price")] |
attribute contains |
//a/@href |
the attribute value itself |
(//p)[1] |
the first match, one-indexed |
//li[last()] |
the last sibling |
Two things trip people up. XPath indexes from one, not zero. And //div[1] does not mean the first div on the page: it means every div that is the first among its siblings. Wrap the expression in parentheses to index the result set.
When to reach for it
Use CSS by default. It is shorter, more readable, and every parser supports it. Reach for XPath when you need a parent, when you need to match on text, or when the only stable anchor on the page is a label next to the value you want.
Mixing the two in one project is normal and sensible. The comparison on the blog goes into the tradeoff in more depth, and the XPath scraping guide works through practical expressions.
One caution: XPath matching on visible text is tied to the site's wording, so a copy change breaks it as surely as a redesign breaks a class-based selector. It is not more durable, only differently fragile.
Where Zenrows fits
The CSS Extractor accepts XPath expressions as well as CSS in the same parameter, so a page needing a parent traversal or a text match does not need a different tool. The advanced selector examples cover tables, lists and nested layouts, which is where XPath usually earns its extra verbosity.
Go deeper on the blog
In the docs
Last updated: Aug 16, 2026