XPath Tester

Test XPath expressions against HTML/XML

Paste HTML or XML, write an XPath expression, and see which nodes it matches. XPath trips people up more than CSS selectors do — a missing / or wrong axis is hard to catch without something to test against. Works with XPath 1.0, which is what Scrapy, lxml, and most Python scraping libraries use under the hood.

HTML / XML

No input provided


How to use

  1. 1Paste HTML or XMLAny fragment works. XML with namespaces is supported too.
  2. 2Write an expressionEnter an XPath expression. For example: //div[@class="price"] or //a/@href.
  3. 3Get resultsIn auto mode, matches update as you type. Disable auto mode to run manually with Run.
  4. 4Use axes for complex queriesparent::, ancestor::, following-sibling:: — for traversing the tree sideways and upward, which CSS cannot do.

Examples

All links on a page
//a/@href
Collect the href of every link on the page in one expression — no element iteration needed.
Element with a specific class
//div[@class="product-title"]
If exact class matching doesn't work — use contains() for partial matching.
Text containing a substring
//p[contains(text(), "price")]
Find a paragraph by its content — CSS can't do this; only XPath with contains(text(), ...).
Parent by descendant text
//li[.//span[@class="badge"]]/a
Select the link inside li knowing only that li contains span.badge — tree traversal without extra conditions.
Nth element in a group
(//ul[@class="results"]/li)[3]
Get exactly the third li in a specific ul — parentheses around the expression are required for position.

When to use for web scraping

XPath is indispensable in two cases where CSS falls short: when you need to find an element by its descendant's text (for example, a table cell next to a "Price" cell), and when you need to navigate up the DOM tree. In Python, XPath 1.0 is supported natively via lxml and Scrapy (response.xpath()). In Node.js — via xmldom and xpath. The tool runs XPath 1.0 — the same version used by these libraries. Typical workflow: get HTML via Request Tester, open it here, write your expression and confirm it returns the right nodes — then copy to your code without surprises.

FAQ

What's the difference between XPath 1.0 and 2.0?
XPath 2.0 adds data types, regular expressions, and many new functions. But most parsing libraries (lxml, Scrapy, xmldom) only support XPath 1.0. This tool runs XPath 1.0 — the same version as your scraper.
Why doesn't //div[@class="name"] work even though the class exists?
The problem is exact matching: @class="name" requires the class attribute to be exactly "name". If the element has multiple classes ("name active"), use contains(@class, "name") instead.
How do I select an attribute instead of an element?
Add /@attribute at the end of the expression: //img/@src returns the src attribute values of all images, not the img tags themselves.
How do I select an element by its descendant's text?
Use a predicate with contains(): //td[contains(., "Price")] returns the td element itself — not the text, not a child, but the td tag as a whole. The dot in the predicate is the filter condition: select td whose text content (including all descendants) contains "Price". To get the adjacent cell of the same row — add /following-sibling::td[1]. CSS can't do this — it's one of XPath's key advantages.
How do I use XPath in Scrapy?
In Scrapy, XPath is called via response.xpath(). To get text: response.xpath('//div[@class="price"]/text()').get(). To get an attribute: response.xpath('//a/@href').getall(). The .get() method returns the first match, .getall() returns a list of all matches. Expressions that work in this tool work in Scrapy without changes — both use XPath 1.0.

Related tools