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().
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:
ABA and BWhen 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']).
Using selector methods with collections of criteria when the collection is treated with OR logic but you need AND logic can cause:
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).
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
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')