Knowledge Base
How to Find Element by Class Using Beautifulsoup
A clear Python tutorial on selecting HTML elements by their class with BeautifulSoup, covering find_all, class_, CSS selectors and multi-class matching.
Knowledge Base
A clear Python tutorial on selecting HTML elements by their class with BeautifulSoup, covering find_all, class_, CSS selectors and multi-class matching.
Filtering by CSS class is the most common way to pinpoint the exact elements you want on a page. Prices, titles, product cards and article bodies almost always carry a class name, so knowing how to target it in BeautifulSoup unlocks most scraping tasks.
This tutorial covers every reliable way to find elements by class, from the class_ keyword to CSS selectors, and explains the gotchas around elements that carry several classes at once.
Beyond class_ and basic selectors, robust class targeting means picking selectors that survive small markup changes, combining class with other attributes when a class alone is ambiguous, and using function filters for logic CSS cannot express. Prefer a stable parent-plus-attribute selector over a brittle auto-generated class, and remember that BeautifulSoup's select runs through SoupSieve, which supports many but not all CSS pseudo-classes.
In Python, class is a reserved keyword, so BeautifulSoup uses class_ (with a trailing underscore) when you filter by it. This small detail trips up a lot of beginners, so it is the first thing to remember.
Use find to get the first matching element. It returns one tag, or None if nothing matches, so guard against the empty case.
import requests
from bs4 import BeautifulSoup
soup = BeautifulSoup(requests.get("https://example.com", timeout=10).text, "html.parser")
price = soup.find("span", class_="price")
if price:
print(price.get_text(strip=True))Use find_all to get a list of every matching element. This is the workhorse for collecting repeated items like product cards or list rows.
cards = soup.find_all("div", class_="product-card")
for card in cards:
title = card.find("h2", class_="title")
print(title.get_text(strip=True) if title else "no title")
If you do not care which tag carries the class, leave the tag out and pass only class_. BeautifulSoup will match any element with that class.
highlighted = soup.find_all(class_="highlight")Many people find CSS selectors more natural, especially when combining a tag and class. Use select for a list and select_one for a single element. The dot prefix denotes a class.
cards = soup.select("div.product-card")
first = soup.select_one(".price")
# Nested: a price inside a card
nested = soup.select("div.product-card span.price")An element like <div class="card featured sale"> has three classes. How you match it depends on what you want.
Passing a single class with class_ matches any element that includes that class, even alongside others. So class_="featured" still finds the div above.
To require that an element has all of a set of classes, a CSS selector chaining them is the cleanest route.
# Must have BOTH classes:
deals = soup.select("div.card.featured")
Passing the complete value with a space matches only elements whose class attribute is exactly that ordered string, which is more brittle and usually not what you want.
# Exact, order-sensitive match:
exact = soup.find_all("div", class_="card featured sale")When class names share a prefix or follow a pattern, a regular expression or CSS attribute selector helps. This is useful for dynamically generated class names.
import re
items = soup.find_all("div", class_=re.compile("^item-"))
# Or with a CSS attribute selector:
items = soup.select('div[class^="item-"]')class=, which raises a syntax error.find without checking for None.Once your class selector works, you will often run it over many URLs, and that volume of requests from one IP can trigger blocks. Rotating proxies keep the job stable, and Cheapest Proxies is our featured value pick worth considering. Comparing providers on rotation, coverage and price ensures you only pay for the capacity your scraper actually uses.
A quick value-first shortlist — Cheapest Proxies leads as the featured pick. Qualitative labels only; confirm exact plans before buying.
| Provider | Best for | Profile | Value |
|---|---|---|---|
| Cheapest Proxies | Budget-conscious buyers comparing affordable proxies | Value Focused | Excellent value |
| Bright Data | Enterprises needing huge pools and compliance controls | Enterprise Focused | Premium |
| Oxylabs | Large-scale scraping and data APIs | Enterprise Focused | Premium |
| Smartproxy (Decodo) | Newcomers who want an easy dashboard | Beginner Friendly | Good |
| SOAX | Precise city and carrier targeting | Automation Friendly | Good |
The most common reason a class-based scraper breaks is that the site shipped a redesign and the class name changed. Utility-first frameworks and component build tools often emit volatile names with hashed or numeric suffixes such as jsx-1a2b3c or css-0t9k. Targeting those directly is a trap. Reach instead for semantic, human-authored hooks: an id, a data-* attribute, an ARIA role, or a class that clearly describes content like price rather than presentation. When you must use a fragile class, anchor it to a stable parent so the selector still narrows correctly even if the leaf class shifts.
A class is frequently shared across many elements, so a class alone returns more than you want. CSS attribute combinators let you tighten the match without inventing brittle chains. Pair a class with a data attribute, a role, or a structural position to isolate exactly one element.
# Class plus a data attribute:
soup.select('div.card[data-type="product"]')
# Class plus an ARIA role:
soup.select('button.btn[role="tab"]')
# Class scoped under a stable id:
soup.select('#results li.row')Some matches are simply not expressible as a selector, for example "a div whose class set contains both an item class and any class starting with a price prefix". BeautifulSoup lets you pass a function to find_all that receives each tag and returns True or False, giving you arbitrary Python logic over the element's classes and attributes.
def is_target(tag):
classes = tag.get("class", [])
return "item" in classes and any(c.startswith("price-") for c in classes)
matches = soup.find_all(is_target)It helps to know that select and select_one delegate to SoupSieve, a dedicated CSS selector library bundled with BeautifulSoup. It supports the selectors most scrapers reach for, including descendant, child, attribute and many pseudo-classes, but it is not a full browser engine and a few exotic pseudo-classes are unavailable. When a selector silently returns nothing, confirming it is valid SoupSieve syntax, rather than assuming the element is missing, often saves a long debugging session.
Start on the smallest sensible tier and scale only what proves itself on your real targets.
Pick the proxy type the task needs first — it drives both success rate and cost more than the logo.
Check traffic limits, rotation rules and what happens on overage before you commit.
Our featured value pick, Cheapest Proxies, is a sensible starting point for affordable comparison.
Selecting by class is free; running that selector at scale is where costs appear, mostly in proxy spend. Comparing providers on value rather than headline numbers keeps a large extraction affordable, and a budget-friendly rotating pool usually handles repetitive class-based scraping just as reliably as a premium plan.
Compare Proxy Zone weighs providers on value, fit and reliability using qualitative judgement — never invented prices, speeds or uptime figures. See our review methodology, or email info@compareproxyzone.com with a correction.
Because class is a reserved keyword in Python, BeautifulSoup adds a trailing underscore so you can filter by class without a syntax error.
Call soup.find_all("div", class_="your-class"), or use the CSS selector form soup.select("div.your-class") to get a list of matches.
Chain them in a CSS selector with no space, like soup.select("div.card.featured"), which requires the element to carry both classes.
Check for typos, confirm the class is in the raw HTML rather than added by JavaScript, and remember that an exact multi-class string match is order-sensitive.
Yes, pass a compiled regular expression to class_, such as re.compile("^item-"), or use a CSS attribute selector like div[class^="item-"].
find returns the first matching element or None, while find_all returns a list of every element that matches your criteria.
For affordable proxies across the main types, our featured value pick is Cheapest Proxies — a strong budget-friendly option worth considering. Check the exact plan before ordering.