Skip to content
Free tool · runs in your browser

CSS Selector Tester

Test a CSS selector against real HTML and see exactly what it matches

class inside class
two classes
attribute then child
positional
attribute starts-with
Product listing
Blog article
Data table
Nested / deep DOM

Matches — 0

No matches. Check the selector against the HTML on the left.

Use this selector in a scraper

Python · parsel

from parsel import Selector

sel = Selector(text=html)
for node in sel.css('.product .title'):
    print(node.get())

Python · BeautifulSoup

from bs4 import BeautifulSoup

soup = BeautifulSoup(html, 'html.parser')
for node in soup.select('.product .title'):
    print(node.get_text(strip=True))

Playwright

for el in page.locator(".product .title").all():
    print(el.inner_text())

Node · Cheerio

const $ = cheerio.load(html);
$('.product .title').each((i, el) => console.log($(el).text().trim()));

Paste a URL and we fetch the raw HTML your scraper would receive, or paste markup directly. Type a CSS selector and see every element it matches. Switch to XPath to compare, and copy the finished selector out as parsel, BeautifulSoup, Playwright or Cheerio code.

What a CSS selector tester is for

Writing a selector is guesswork until you run it. You think .product .price will pick up every price on a listing page, then your scraper returns three results instead of forty because half the prices sit inside a different wrapper. This tool closes that loop: paste the HTML, write the selector, and see the matches immediately — before the selector goes anywhere near a scraper.

The matching here is done by the browser's own querySelectorAll, not an approximation, so what you see is exactly what your code will get.

CSS selector syntax cheat sheet

SelectorMatches
.priceevery element with class price
article.product<article> elements that also have class product
.product .pricea .price anywhere inside a .product
.product > .pricea .price that is a direct child of .product
[data-id]any element with a data-id attribute
[data-id='a2']exact attribute value
a[href^='/p/']attribute starts with
a[href$='.pdf']attribute ends with
a[href*='product']attribute contains
tr:nth-child(2)the second tr among its siblings
li:first-child / li:last-childfirst / last sibling
p:not(.intro)every p except those with class intro
h2, h3either (a selector list)
.a + .b.b immediately after .a
.a ~ .bany .b that follows .a

CSS or XPath — which should you use?

Use CSS for almost everything. It is shorter, it is what browser devtools give you, and every scraping library supports it.

Reach for XPath when you need something CSS genuinely cannot express:

  • Selecting by text content. //a[contains(text(),'Next')] has no CSS equivalent.

  • Walking upward. XPath has parent:: and ancestor::; CSS has no parent selector that works in scraping libraries.

  • Selecting an attribute itself rather than the element, e.g. //a/@href.

Everything else is easier in CSS. The tester above switches between the two so you can compare the same intent in both.

Why a selector works in devtools but not in your scraper

This is the most common scraping bug, and the cause is nearly always the same: the page you inspected is not the page your scraper downloaded.

Devtools shows you the DOM *after* JavaScript has run. A plain HTTP request returns the raw HTML *before* it runs. If the content is rendered client-side, your selector matches nothing because the elements do not exist yet in what you fetched.

The URL box above exists to settle this in one step. Paste the page address and it loads the raw HTML the server returned — the same bytes requests.get() or curl would hand you. If your selector matches there, the selector is fine. If it matches in devtools but not here, the content is JavaScript-rendered and no amount of fixing the selector will help.

Three things to check, in order:

  1. Load the URL above, or view the raw sourceview-source: in the URL bar, or curl the page. If your target is not in there, the content is JavaScript-rendered.
  2. Check for iframes. Content inside an <iframe> is a separate document; a selector on the parent will never reach it.
  3. Look for generated class names. Framework builds emit classes like css-1x2y3z that change on every deploy. Anchor on a stable attribute — data-testid, an id, or the element structure — instead.

If the content is JavaScript-rendered, you need something that executes the page rather than just downloading it. That is what a browser-based agent does, and it is why browser automation reaches pages that a plain HTTP scraper cannot.

From a working selector to a running scraper

Once the selector matches what you want, the code panel above gives you the same selector in parsel, BeautifulSoup, Playwright and Cheerio. That handles a page.

The part nobody writes about is what happens on page two hundred: pagination, rate limits, sessions that expire, layouts that change on a Tuesday, and the retry logic around all of it. An Autonoly agent runs the selector on a schedule, handles the pages behind logins, and writes results straight to a sheet or database — so the selector stays the only part you have to think about.

Run this selector on a schedule

Let an AI agent do this automatically, on a schedule, across thousands of records.

Try Autonoly free

FAQ

Common Questions

Everything you need to know about CSS Selector Tester.

Other free tools

Related automations, guides and terms

Where this shows up in real workflows.

DefinitionBrowser AutomationBrowser automation is the use of software to control a web browser programmatically, performing tasks like clicking buttons, filling forms, and extracting data without manual human interaction.DefinitionCSS SelectorA CSS selector is a pattern used to identify and target specific HTML elements on a web page, widely used in browser automation to locate buttons, forms, text, and other interactive elements.DefinitionRetry LogicRetry logic is a fault-tolerance pattern that automatically re-attempts failed operations with configurable delays, backoff strategies, and maximum attempt limits to handle transient errors without manual intervention.GuideWhy Your Scraper Returns an Empty List (and How to Fix It)Your selector works in Chrome and returns nothing in Python. Almost always the page you inspected is not the page you downloaded. A 30-second diagnosis, the six real causes, and the fix for BeautifulSoup, Scrapy, Playwright and lxml.GuideAutonoly vs Browse.ai: Web Scraping vs Full Browser AutomationAn honest comparison of Autonoly and Browse.ai. Browse.ai excels at simple web scraping and change monitoring. Autonoly handles the full spectrum — scraping, form filling, document processing, and multi-step workflows. We break down when each tool is the right choice.GuideAxiom.ai vs Browserflow vs Autonoly: No-Code Browser Automation ComparedA three-way comparison of Axiom.ai, Browserflow, and Autonoly for no-code browser automation. Axiom wins for simple Chrome macros, Browserflow wins for Make.com users, and Autonoly wins for AI-driven complex workflows. We break down features, pricing, and use cases.