Use .next_sibling or .previous_sibling to move to the immediately adjacent node in Beautiful Soup’s parse tree. For the next or previous matching tag—skipping intervening whitespace and other non-matching nodes—use find_next_sibling() or find_previous_sibling(). To retrieve several matches, use the plural methods or iterate over .next_siblings and .previous_siblings.
Parse the HTML and find your starting node
Sibling navigation starts with a parsed tree and a node to navigate from. In this example, the target is the summary paragraph inside a card:
from bs4 import BeautifulSoup
html = '''
<div class="card">
<h2>Title</h2>
<p class="summary">Summary</p>
<p class="details">Details</p>
</div>
'''
soup = BeautifulSoup(html, "html.parser")
summary = soup.find("p", class_="summary")
if summary is None:
raise ValueError("Could not find the summary paragraph")
Passing "html.parser" explicitly makes the parser choice visible in the code. Beautiful Soup’s documentation warns that parser choice can affect the resulting tree; malformed markup or a different parser can therefore change which nodes are adjacent. The documentation page identifies Beautiful Soup 4.14.3.
Once you have the target, choose a sibling method based on whether you need the physically adjacent node or the closest node that matches a condition.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →#1 Best Overall
Get the immediately adjacent node
The singular properties return the adjacent node in the same parent’s child list:
next_node = summary.next_sibling
previous_node = summary.previous_sibling
print(repr(next_node))
print(repr(previous_node))
These properties do not mean “next tag.” They mean the next or previous node, whatever its type. With indented HTML, the node adjacent to a tag is often a NavigableString containing a newline and spaces. Punctuation or other text between tags can also be returned.
For example, the next node after the summary paragraph in the sample markup may be the newline and indentation before the details paragraph, rather than the details tag itself. This is why printing repr() is useful when inspecting an unexpected result: it makes whitespace visible.
Skip text nodes when you need the next tag
If you specifically need the next tag in sibling order, advance past strings before using the result:
from bs4 import NavigableString
node = summary.next_sibling
while node is not None and isinstance(node, NavigableString):
node = node.next_sibling
if node is not None:
print(node.name)
print(node.get_text(" ", strip=True))
The loop checks for None because there may be no later sibling. It also skips all NavigableString nodes, not only indentation. If meaningful text between tags matters to your extraction, do not discard it this way; use a matching method when the task is to find a particular tag.
Rank #2
Find the nearest matching sibling
For most extraction tasks, a matching method is clearer than manually stepping through adjacent nodes. find_next_sibling() searches later siblings and returns the first match; find_previous_sibling() searches earlier siblings in reverse document order.
next_paragraph = summary.find_next_sibling("p")
previous_heading = summary.find_previous_sibling("h2")
if next_paragraph is not None:
print(next_paragraph.get_text(" ", strip=True))
if previous_heading is not None:
print(previous_heading.get_text(" ", strip=True))
In the sample card, the next matching paragraph is the details paragraph. The method can move past whitespace strings and other nodes that do not match the requested tag. It does not search arbitrary descendants or the whole document: it looks among siblings of the starting node.
Filter by a class or attribute
Matching methods accept the same kinds of filters used by Beautiful Soup’s search methods, including tag names, attributes, strings, and keyword attribute filters:
# The first later paragraph with this class
next_detail = summary.find_next_sibling("p", class_="details")
# A preceding table row with a particular attribute
previous_row = cell.find_previous_sibling(
"tr", attrs={"data-state": "ready"}
)
Use class_ for a class filter in Python code. For attributes that do not map conveniently to keyword arguments, pass an attrs dictionary. If no sibling matches, the singular method returns None; check that result before accessing tag properties such as .name or calling .get_text().
Collect all matching siblings
Use the plural methods when you need every matching sibling in one direction. They return a list and accept a limit as well as the matching filters:
all_paragraphs_after = summary.find_next_siblings("p")
all_paragraphs_before = summary.find_previous_siblings("p")
first_three_links = first_link.find_next_siblings(
"a", class_="sister", limit=3
)
For a matching-method search, the returned values are the matching tags, not the intervening whitespace nodes. Use find_next_siblings() when you need to filter to particular tags or attributes; use the sibling generators when you want to inspect every node, including strings.
Iterate over every later or earlier node
for node in summary.next_siblings:
print(repr(node))
for node in summary.previous_siblings:
print(repr(node))
These generators include strings as well as tags. If the loop should act only on tags, test the node type:
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problemsfor node in summary.next_siblings:
if getattr(node, "name", None) is not None:
print(node.name, node.get_text(" ", strip=True))
The first yielded node is the nearest sibling in that direction. The plural matching methods and generators answer different questions: one returns all nodes matching your filters, while the other lets you examine the full sequence and decide what to keep.
Know what counts as a sibling
Two nodes are siblings when they have the same parent in the parsed tree. They are not necessarily adjacent in the source text once whitespace and other nodes are counted, and nodes that merely appear next to one another on a rendered page are not necessarily siblings.
For example, the text inside a <b> tag and the text inside a neighboring <c> tag have different parents: each text node belongs to its own tag. The tags may be siblings, but their contents are not siblings of each other. Start navigation from the element whose position matters, then verify that the intended target shares its parent.
Sibling navigation is not document-order navigation
.next_sibling stays among nodes under the same parent. .next_element follows document order instead, which can descend into a node’s children and then continue elsewhere in the tree. If a sibling lookup seems to stop before a visible element, check the HTML structure and parent relationships rather than substituting .next_element without considering that broader traversal.
Choose the right method
| Need | Use | What to expect |
|---|---|---|
| The adjacent node only | .next_sibling or .previous_sibling |
May return whitespace, punctuation, text, or a tag. |
| The nearest later or earlier matching tag | .find_next_sibling() or .find_previous_sibling() |
Returns the first sibling that matches your filters, or None. |
| All matching siblings in one direction | .find_next_siblings() or .find_previous_siblings() |
Returns matching siblings as a list; filters and a limit can narrow the result. |
| Every intervening node in order | .next_siblings or .previous_siblings |
Yields tags and strings so you can apply your own logic. |
Troubleshoot unexpected results
next_sibling returns a newline or spaces
This is expected for indented HTML: the immediate node is often a whitespace string. Use find_next_sibling("tagname") for the closest matching tag, or explicitly advance over strings if you need direct navigation.
A matching method returns None or an empty list
Check the starting node, direction, tag name, and filters. The desired element may be a descendant, an ancestor, or elsewhere in the document rather than a sibling. Also check whether its attribute value matches the filter you supplied.
The result changes with the parser
Beautiful Soup builds a tree from the source, and parser choice can produce a different parse tree. Keep the parser explicit, inspect the parsed structure and parent relationships, and confirm that your input markup is well formed enough for the structure you expect.
The code fails when no match exists
A singular matching method can return None. Guard against that before calling tag methods:
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallCrashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteBest Value
match = summary.find_next_sibling("p", class_="details")
if match is None:
print("No matching later paragraph")
else:
print(match.get_text(" ", strip=True))
A loop misses text or removes too much
Sibling generators include strings, while a loop that skips every NavigableString discards both whitespace and meaningful text. Decide whether your task needs only tags or needs to preserve text nodes, and filter accordingly.
Or skip the browser setup
If your actual goal is to capture a webpage rather than inspect its parsed HTML yourself, ScreenshotNeo offers a screenshot API and MCP server. Its clean-shot options accept cookie or consent banners and remove more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and responses identify the page verdict and billing status in headers. AI agents can use its MCP server tools to take screenshots, get page information, or capture PDFs. The free plan includes 1,000 screenshots a month without a card; paid plans start at $5 for 3,000 screenshots.
Here is a one-request cURL example that saves a WebP screenshot:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo API documentation for setup and request options. ScreenshotNeo also supports PNG, JPEG, or PDF output and options such as full-page capture, selector-based capture, viewport and device settings, and custom CSS or JavaScript. Sign up for ScreenshotNeo to get 1,000 free screenshots a month, with no card required.
Frequently Asked Questions
Can I use a CSS selector to find a sibling in Beautiful Soup?
The sibling methods use Beautiful Soup’s tag, attribute, string, and keyword filters; they are not CSS-selector methods.
Do the plural sibling methods return generators?
No. The plural find methods return a list of matching siblings. The properties .next_siblings and .previous_siblings provide iterators over sibling nodes.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

