This rule raises an issue when a list of class names is passed to a parameter that filters HTML elements by CSS class and expect to match elements that have ALL the specified classes.

In Python’s BeautifulSoup library, this specifically refers to the class_ parameter accepted by many search methods, including find(), find_all(), find_parent(), find_parents(), find_next_sibling(), find_next_siblings(), find_previous_sibling(), find_previous_siblings(), find_next(), find_all_next(), find_previous(), and find_all_previous().

Why is this an issue?

In HTML parsing libraries, when you pass multiple class names to element selection methods that accept a list of classes, these methods often use OR logic. This means they select elements that have ANY of the specified classes, not ALL of them.

This behavior is counter intuitive for developers familiar with CSS selectors, where chaining class selectors (like .A.B) means the element must have both classes. The OR logic can lead to selecting more elements than intended, causing bugs in data extraction or processing.

For example, if you want to find all div elements with both class A and class B, passing both class names as separate items in a list to the selection method will actually find all div elements with class A OR class B. This includes:

When you actually need elements with both classes, this leads to incorrect results.

In BeautifulSoup specifically, this occurs when using the class_ parameter with a list of class names in any of these methods. For example: soup.find_all('div', class_=['A', 'B']) or soup.find('div', class_=['A', 'B']).

What is the potential impact?

Using selector methods with collections of criteria when the collection is treated with OR logic but you need AND logic can cause:

How to fix it

If you need elements that have ALL of the specified classes, replace the class_ parameter list with the select() method using chained CSS class selectors. Chain class selectors by placing them directly after each other with a dot prefix (e.g., .A.B).

If you actually need elements that have ANY of the specified classes (OR logic), use a comma-separated CSS selector instead (e.g., div.A, div.B).

Code examples

Noncompliant code example

from bs4 import BeautifulSoup

soup = BeautifulSoup(html, 'html.parser')
# Intending to select divs with both class A AND class B, but actually selects A OR B
results = soup.find_all('div', class_=['A', 'B'])  # Noncompliant

Compliant solution

When you need elements that have all of the specified classes (AND logic):

from bs4 import BeautifulSoup

soup = BeautifulSoup(html, 'html.parser')
# Selects divs with both class A AND class B
results = soup.select('div.A.B')

When you actually need elements that have any of the specified classes (OR logic), use a comma-separated CSS selector:

from bs4 import BeautifulSoup

soup = BeautifulSoup(html, 'html.parser')
# Selects divs with class A OR class B
results = soup.select('div.A, div.B')

Resources

Documentation