Glowing Web Network
Glowing Web Network

CSS Selector Tester — Test Selectors Against Any HTML

Paste HTML, type a CSS selector, see every match highlighted live. Runs querySelectorAll in your browser — no server round-trip.

Need structural queries or text matching? Try the XPath Tester.

CSS selector • runs 100% in your browser
0 matches

Try a sample:

No matches.

What CSS selectors are and how scrapers use them

CSS selectors are pattern expressions that pick HTML elements by tag, class, id, attribute, position, or relation. Originally designed for styling, they've become the default DOM query language thanks to document.querySelectorAll. Every scraping library worth its salt — BeautifulSoup (via SoupSieve), parsel, lxml's cssselect, Cheerio in Node — accepts the same selector syntax.

For most scraping work CSS is the right starting point: it's terse, readable, and fast. Use the panel above to draft a selector against real HTML, then drop it into your scraper.

CSS selector cheat sheet

PatternWhat it matchesExample
tagAll elements with this taga → every link
.classElements with this class.product-title
#idElement with this id#cart
A BB descendants of Aarticle p
A > BB direct children of Aul > li
A + BB immediately after A (sibling)h2 + p
A ~ BB siblings after Ah2 ~ p
[attr]Has attributea[rel]
[attr=val]Attribute equals valueinput[type="email"]
[attr^=val]Attribute starts witha[href^="https://"]
[attr$=val]Attribute ends withimg[src$=".webp"]
[attr*=val]Attribute contains substringspan[class*="price"]
:nth-child(n)nth child of its parenttr:nth-child(odd)
:not(sel)Does not matcha:not([rel])
:has(sel)Has a matching descendant (modern only)article:has(img)

CSS selectors vs XPath — which to use

CSS wins on readability and tooling. article > h2.title reads cleanly, every browser DevTools panel speaks CSS, and the selectors compose 1:1 between styling and scraping. For querying by class, attribute, position, or simple structure, CSS is shorter and faster than the XPath equivalent.

XPath wins when you need things CSS can't express: matching by text content, moving up the tree (parent / ancestor), or selecting attributes themselves rather than the elements that bear them. See the XPath Tester for the same SERP-grade tooling on the other side of the fence.

Using CSS selectors in Python (BeautifulSoup + parsel)

BeautifulSoup ships .select() for CSS via SoupSieve. parsel (used by Scrapy) supports both .css() and .xpath().

# pip install beautifulsoup4 parsel requests
import requests
from bs4 import BeautifulSoup
from parsel import Selector

resp = requests.get("https://example.com")

# BeautifulSoup (via SoupSieve)
soup = BeautifulSoup(resp.content, "html.parser")
titles = [el.get_text(strip=True) for el in soup.select("a.product-title")]

# parsel (used by Scrapy)
sel = Selector(text=resp.text)
prices = sel.css('span[class*="price"]::text').getall()
print(titles, prices)

Common pitfalls

:has() is modern-only

:has() only landed in Safari (2022), Chrome (2022), and Firefox (2023). Older browsers and lxml's cssselect throw a SyntaxError. SoupSieve added :has support, but Scrapy users on parsel<1.8 won't have it. Test in a current browser first; the tester above uses your browser's engine so the support window matches reality.

Class names with multiple values

.price matches the WHOLE class token list, but [class="price"] only matches if class is EXACTLY "price". An element with class="price price--current" matches .price but not [class="price"]. Use [class~="price"] for an exact word match.

Whitespace inside selectors

article > p (with extra spaces) parses fine, but article >p without space after > also parses fine in modern engines and was historically buggy. Stick to single-space separators to keep selectors portable across older Python parsers.

:nth-child vs :nth-of-type

:nth-child(2) is "second child of its parent regardless of tag". :nth-of-type(2) is "second of THIS tag among its siblings". If your <p> is the second child but the first <p>, :nth-child(2) matches; :nth-of-type(1) does too — they target different things.

FAQ

What's the difference between querySelector and querySelectorAll?
querySelector returns the FIRST match (or null). querySelectorAll returns a NodeList of every match (never null, may be empty). For scraping you almost always want querySelectorAll so you don't silently drop additional results.
Are CSS selectors case-sensitive?
Tag names and attribute names are matched case-insensitively in HTML documents (case-sensitively in XML). Class names, ids, and attribute VALUES are case-sensitive by default. To match values case-insensitively, use the [attr="val" i] flag (modern engines only): a[href*="login" i].
Can I use :has() in BeautifulSoup?
Yes, since SoupSieve 2.1 (BeautifulSoup 4.9+). soup.select("article:has(img)") works. If you're on an older version, upgrade or use a two-step query: find articles, then filter those whose .find("img") is not None.
Why doesn't my :nth-child selector work?
Usually because :nth-child counts ALL siblings, not just the ones matching your earlier selector. ul li:nth-child(2) means "an li that's the 2nd child of its parent" — if the first child is something else (a <span>, say), no li matches. Use :nth-of-type(2) to count "2nd of this tag among siblings".
What's the most efficient CSS selector for scraping?
For scraping in Python, the engine bottleneck almost never matters next to network I/O. That said, id selectors and class selectors are fastest; deeply nested descendant chains (a b c d) are slowest. Anchor on the most specific stable attribute you can find (data-* attributes are usually safer than class names which can churn).
footer-frame

Start building with Scrappey

Try It For Free. No Subscription Required. No Credit Card Required. Instant Set-Up. 150 Free Requests Are Waiting For You!