Last modified: Aug 31, 2026
BeautifulSoup Class Contains: Easy Selector Guide
Web scraping often requires finding elements by their CSS class. But sometimes, class names change or have multiple values. You need a flexible way to match them. This is where "class contains" logic becomes essential.
BeautifulSoup offers powerful methods to handle this. You can use CSS selectors or the find_all method with custom filters. This guide shows you both approaches with practical examples.
You will learn how to target elements with partial class names. This is useful for dynamic sites and modern CSS frameworks. Let's dive into the cleanest ways to do it.
Using CSS Selectors for Partial Matches
CSS selectors are the most readable way to find elements by class. The select method supports standard CSS syntax. For partial matches, you use the attribute selector syntax.
To match a class that contains a specific string, use [class*="value"]. This finds any element whose class attribute includes your string anywhere. It's simple and effective.
Here is a basic example using a sample HTML snippet. We will parse it and find all elements with "card" in their class attribute.
from bs4 import BeautifulSoup
html_doc = """
<div class="product-card">Product 1</div>
<div class="card special">Product 2</div>
<div class="product">Product 3</div>
"""
soup = BeautifulSoup(html_doc, 'html.parser')
# Find all elements where class contains "card"
cards = soup.select('[class*="card"]')
for card in cards:
print(card.text)
This code finds the first two divs. The third one, "product", does not match because "card" is not in its class string. The output is clean and shows only the relevant items.
Product 1
Product 2
This method is case-sensitive. If you need case-insensitive matching, you must use a different approach with find_all.
Using find_all with a Custom Function
The find_all method is more flexible. You can pass a function as the class_ argument. This function receives the class attribute value, which is a list of classes.
You can then check if any class in that list contains your target string. This gives you full control over the matching logic. It's perfect for complex conditions.
Let's see how to find elements where any single class contains "btn". This is great for buttons with dynamic modifiers.
from bs4 import BeautifulSoup
html_doc = """
<button class="btn-primary">Save</button>
<button class="secondary-button">Cancel</button>
<button class="btn">Submit</button>
"""
soup = BeautifulSoup(html_doc, 'html.parser')
def class_contains_btn(class_list):
if class_list:
return any("btn" in c for c in class_list)
return False
buttons = soup.find_all(class_=class_contains_btn)
for button in buttons:
print(button.text)
This finds the first and third buttons. The second one, "secondary-button", does not contain "btn". The function checks each class individually.
Save
Submit
This approach is more verbose but offers precise control. You can also use regular expressions for even more power.
Using Regular Expressions for Advanced Matching
For complex patterns, regular expressions are your best friend. You can pass a compiled regex object to the class_ parameter in find_all. This allows you to match exact substrings or complex patterns.
For instance, to match classes that start with "nav" or end with "menu", you can use regex. This is more efficient than writing multiple manual checks. It keeps your code clean.
Here is an example using re.compile to find elements with "price" anywhere in their class list. This is handy for e-commerce sites.
from bs4 import BeautifulSoup
import re
html_doc = """
<span class="old-price">$10</span>
<span class="new-price special">$8</span>
<span class="discount">$5</span>
"""
soup = BeautifulSoup(html_doc, 'html.parser')
# Match any class containing "price"
price_elements = soup.find_all(class_=re.compile("price"))
for el in price_elements:
print(el.text)
This finds the first two spans. The regex searches each class string in the list. It matches "old-price" and "new-price".
$10
$8
Regular expressions are powerful but can be slower on large documents. Use them wisely when simple string checks are not enough.
Handling Multiple Classes Correctly
Remember that in BeautifulSoup, the class_ attribute is a list. When you use select with [class*="value"], it looks at the entire string of the class attribute. This can cause issues if you have multiple classes.
For example, class="product-card" and class="card product" are different. The CSS selector matches the full string. The find_all with a function checks each class separately.
Choose the method that fits your data structure. If you need to match a specific class in a multi-class list, use find_all with a function. If you want a simple substring match, CSS selectors are fine.
Consider this example to see the difference clearly. We have an element with multiple classes and want to find only those with "active".
from bs4 import BeautifulSoup
html_doc = """
<div class="content active">Visible</div>
<div class="content-active">Hidden</div>
"""
soup = BeautifulSoup(html_doc, 'html.parser')
# CSS selector matches both because string contains "active"
css_match = soup.select('[class*="active"]')
print("CSS Matches:", [el.text for el in css_match])
# Function matches only the exact class "active"
def has_active(class_list):
return class_list is not None and "active" in class_list
func_match = soup.find_all(class_=has_active)
print("Function Matches:", [el.text for el in func_match])
The output shows the difference. The CSS selector is broader. The function is more precise. Choose based on your needs.
CSS Matches: ['Visible', 'Hidden']
Function Matches: ['Visible']
Practical Use Case: Scraping Dynamic Content
Modern websites often use CSS frameworks like Tailwind or Bootstrap. These generate long, dynamic class names. Hardcoding exact class names is fragile. Using "contains" logic makes your scraper robust.
For example, a site might have buttons with classes like button-primary and button-secondary. You can use [class*="button-"] to get all of them. This saves time and reduces maintenance.
Let's simulate a realistic scenario. We have a list of products with varying class names. We want to extract all product titles.
from bs4 import BeautifulSoup
html_doc = """
<div class="product-item">Laptop</div>
<div class="item product-sale">Mouse</div>
<div class="product">Keyboard</div>
<div class="featured-product">Monitor</div>
"""
soup = BeautifulSoup(html_doc, 'html.parser')
# Find all elements that have "product" somewhere in their class list
products = soup.find_all(class_=lambda c: c and any("product" in x for x in c))
for product in products:
print(product.text)
This captures all four items. The lambda function checks each class in the list. It's a concise way to write the filter.
Laptop
Mouse
Keyboard
Monitor
This pattern is common in real-world scraping. It handles variations in class naming conventions. You can adapt it to your specific site.
Performance Tips for Large Pages
When scraping large HTML documents, performance matters. CSS selectors are usually faster than custom functions. They are implemented in C under the hood. Use select for simple contains checks.
If you need a custom function, keep it simple. Avoid heavy regex patterns unless necessary. Compile your regex once outside the loop if you use it many times.
Also, consider narrowing your search first. For example, find all div tags first, then filter by class. This reduces the number of elements the function processes.
Here is a quick performance tip. Use select for speed and find_all for precision. Test both on your target page to see which is faster.
For more advanced techniques, you might want to check our guide on BeautifulSoup vs Scrapy. It helps you decide when to use a full framework.
Common Pitfalls and How to Avoid Them
One common mistake is assuming class names are unique. They are not. Multiple elements can share the same class. Always iterate over the results and handle multiple matches.
Another pitfall is forgetting that the class_ attribute is a list. When you use get('class'), it returns a list. If you use ['class'], it returns a string. Know the difference.
Also, be careful with special characters in class names. CSS selectors require escaping for certain characters. Use find_all with a function to avoid escaping issues.
If you are new to BeautifulSoup, our BeautifulSoup Cheat Sheet is a great resource. It covers all the basics and advanced methods.
Finally, always test your selectors on a live page. Use your browser's developer tools to inspect the HTML. This ensures your code works with the actual structure.
Conclusion
Mastering "class contains" in BeautifulSoup is crucial for effective web scraping. You have multiple tools at your disposal. CSS selectors offer simplicity and speed. Custom functions provide precision and flexibility.
Regular expressions add another layer of power for complex patterns. Choose the method that best fits your specific use case. Remember to test your code on real HTML to ensure accuracy.
Use these techniques to build robust scrapers that handle dynamic class names. This will save you time and frustration in the long run. Happy scraping!
For more tips, explore our article on checking if a tag exists to improve your error handling.