Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

Use Selenium’s plural lookup and test the returned list: driver.find_elements(By.CSS_SELECTOR, "#target") is truthy when at least one matching node exists and false when the list is empty. This is the cleanest immediate existence check because it does not require catching an exception.

That check describes the DOM at one instant. For JavaScript-rendered content, use an explicit wait for presence (or visibility when that is what your test needs) before branching or interacting.

The immediate existence check

Import By, locate all matching nodes, and let Python evaluate the collection’s truthiness:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
from selenium.webdriver.common.by import By

matches = driver.find_elements(By.CSS_SELECTOR, "#target")

if matches:
    print("Element exists in the current DOM")
else:
    print("No matching element was found")

find_elements returns a collection of matching WebElement objects. If nothing matches, Selenium returns an empty list, which Python treats as false. If one or more nodes match, the list is true. The test therefore answers “does at least one node matching this locator exist right now?” without exception handling.

Check how many elements matched

When uniqueness matters, inspect the length instead of only using truthiness:

matches = driver.find_elements(By.ID, "target")

if len(matches) == 0:
    print("Missing")
elif len(matches) == 1:
    print("Exactly one match")
else:
    print(f"Unexpectedly found {len(matches)} matches")

A true result alone does not prove that the locator is unique. A broad CSS selector can match several nodes, including hidden duplicates or repeated components.

When to use find_element instead

Use the singular method when your next operation needs one expected element. Selenium returns the first matching WebElement. If no element matches, the lookup raises NoSuchElementException.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.common.by import By

try:
    element = driver.find_element(By.ID, "target")
except NoSuchElementException:
    element = None

if element is not None:
    print("Found the first matching element")
    # element.click(), element.text, or another operation can follow
else:
    print("The expected element is absent")

This form is useful when absence is exceptional for the test or when you immediately need the returned object. For a simple optional branch, find_elements is usually clearer because “none found” is represented by an empty collection rather than an exception.

Do not hide real failures with a broad exception

Catch NoSuchElementException for the singular lookup. Avoid catching every exception around the lookup: a malformed locator, a browser problem, or a later interaction failure is a different defect and should remain visible.

Choose a locator that identifies the intended node

The Python API supports ID, name, XPath, CSS selector, class name, tag name, link text, and partial link text strategies. Pick the most stable attribute available in the page under test.

Need Pattern What it establishes
Branch on whether a match exists now bool(driver.find_elements(By.ID, "target")) At least one node matched at lookup time, or none did.
Retrieve one expected match driver.find_element(By.ID, "target") The first matching WebElement; no match raises NoSuchElementException.
Use a CSS selector driver.find_elements(By.CSS_SELECTOR, "form#login input[name='email']") All nodes matching the selector in the current search context.
Search inside an existing element card.find_elements(By.CLASS_NAME, "price") Matches within that WebElement, rather than the whole document.

A locator can be syntactically valid yet point at the wrong node. If the check unexpectedly returns zero or many matches, inspect the selector and the current page state before changing the wait.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Handle elements that appear after navigation or interaction

A one-time lookup reports only the state at the moment it runs. If application JavaScript adds the element later, wait on a condition instead of guessing with a fixed sleep.

Wait for DOM presence

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

locator = (By.CSS_SELECTOR, "#target")

 element = WebDriverWait(driver, 10).until(
    EC.presence_of_element_located(locator)
)
print("A matching node is now present:", element)

presence_of_element_located waits until a matching element is present in the DOM and returns the WebElement. WebDriverWait.until keeps polling until the condition is truthy. If the timeout expires, Selenium raises TimeoutException.

The documented default polling interval is 0.5 seconds, and the wait’s default ignored exception is NoSuchElementException. The example’s 10-second timeout is a bounded choice; set it to the maximum delay your application legitimately needs rather than waiting indefinitely.

Convert a wait timeout into an existence result

from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

locator = (By.ID, "target")

try:
    element = WebDriverWait(driver, 10).until(
        EC.presence_of_element_located(locator)
    )
except TimeoutException:
    element = None

if element is None:
    print("The element did not become present within 10 seconds")
else:
    print("The element exists in the DOM")

This pattern is appropriate when absence after the deadline is an expected branch. If the element is mandatory, allow the timeout to fail the test so the failure is reported instead of silently converted into a pass.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Presence is not visibility

Presence means that Selenium found a node in the DOM. It does not necessarily mean that the node is visible. A hidden menu item, an off-screen component, or an element with no rendered size can satisfy a presence condition.

Wait until Selenium considers it visible

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

locator = (By.CSS_SELECTOR, "#target")
visible_element = WebDriverWait(driver, 10).until(
    EC.visibility_of_element_located(locator)
)
visible_element.click()

Selenium’s visibility condition requires the element to be displayed and to have nonzero height and width. Use it when the test’s requirement is “the user can see this,” not merely “the node has been inserted.” Even a visible element may still be unsuitable for a particular action, so keep action-specific checks separate.

Pick the condition that matches the assertion

  • DOM existence: use find_elements for an immediate snapshot or presence_of_element_located for a bounded wait.
  • Rendered visibility: use visibility_of_element_located.
  • Optional content: use a plural lookup and branch on whether the list is empty.
  • Required content: use a wait and let TimeoutException identify a failed expectation.

Implicit and explicit waits

Selenium provides both implicit and explicit wait mechanisms. For a particular event such as presence or visibility, a bounded explicit wait states exactly what the test is waiting for and keeps the timeout next to the assertion.

Do not rely on an assumed formula for combining implicit and explicit waits. The effective interaction can depend on the installed Selenium version and the individual calls. If a project uses both mechanisms, consult the waits documentation for that version and keep timing behavior consistent across the suite.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Reusable helper functions

Immediate check

from selenium.webdriver.common.by import By

def element_exists(driver, locator):
    """Return True when locator matches at least one current DOM node."""
    return bool(driver.find_elements(*locator))

if element_exists(driver, (By.CSS_SELECTOR, "#target")):
    print("Present now")

Waited check with an explicit deadline

from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

def element_appears(driver, locator, timeout=10):
    try:
        WebDriverWait(driver, timeout).until(
            EC.presence_of_element_located(locator)
        )
        return True
    except TimeoutException:
        return False

if element_appears(driver, ("css selector", "#target"), timeout=10):
    print("Appeared before the deadline")

Passing a locator tuple keeps the selector strategy and value together. In normal code, use the named By constants, for example (By.CSS_SELECTOR, "#target"); the string form in the final example is equivalent to Selenium’s CSS strategy.

Or skip the browser setup

If your goal is a visual record of a page rather than a DOM assertion, ScreenshotNeo provides a single-request website screenshot API. It accepts consent banners as a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each cleanup step can be turned off. Only clean shots are billed, while bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers.

For API details, see the ScreenshotNeo documentation. A complete cURL request is:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

ScreenshotNeo also has an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to try it.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Equivalent calls from Python and Node.js

Python

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

These calls capture a rendered image; they do not replace Selenium when you need to inspect a locator, wait for a state, or assert against the DOM.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshooting existence checks

The check returns an empty list, but the page appears to contain the element

  • Timing: the lookup ran before JavaScript inserted the node. Replace the one-time call with WebDriverWait(...).until(EC.presence_of_element_located(...)).
  • Locator mismatch: verify the ID, class, tag, link text, XPath, or CSS selector and check for spelling and quoting errors.
  • Wrong search context: a lookup from a WebElement searches only that element’s descendants. Search from driver when the target is elsewhere, or from the correct container when scoping is intentional.

The presence wait succeeds, but clicking or reading it fails

Presence proves DOM insertion, not visibility or action readiness. Use visibility_of_element_located when the requirement is display, then apply the checks required by the specific action.

The singular lookup raises NoSuchElementException

That is Selenium’s documented result when find_element has no match. Use the plural lookup for an ordinary optional branch, or add a bounded explicit wait when the element is expected to arrive asynchronously.

The explicit wait raises TimeoutException

At least one poll completed without satisfying the condition before the deadline. Check the locator and page state first; increase the timeout only when the application’s legitimate load time requires it. If the element is optional, catch the timeout and return a false result as shown above.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A previously stored element no longer behaves reliably

Dynamic pages can replace nodes after you locate them. Locate again when the page updates, and use a condition against the current locator rather than assuming an earlier reference remains attached forever.

Version and documentation note

The Python WebDriver and finder pages reviewed for this guidance are displayed as Selenium 4.49.0 documentation, while the expected-conditions page is displayed as Selenium 4.33.0. Those displayed versions are not a promise that every installed package has identical signatures. Check the documentation corresponding to the Selenium version installed in your project before depending on version-specific behavior.

A practical decision sequence

  1. Define what “exists” means for the test: a node in the DOM, a visible element, or an element ready for a particular action.
  2. Choose a stable locator and decide whether multiple matches are acceptable.
  3. For a current snapshot, call find_elements and test the list.
  4. For one required element, call find_element or wait for a condition that returns it.
  5. For late-loading content, use a bounded explicit wait instead of a fixed sleep.
  6. When a wait fails, inspect timing, locator, and search context before changing the assertion.

Frequently Asked Questions

Does a true find_elements result guarantee that the locator is unique?

No. It guarantees only one or more matches. Compare len(matches) with 1 when the test requires exactly one node.

What does Selenium return when a plural lookup finds nothing?

An empty collection (an empty Python list), which evaluates to False in a conditional.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Which Selenium documentation version should I follow?

Match the documentation to the package installed in your project. The finder and WebDriver pages surfaced as 4.49.0, while the expected-conditions page surfaced as 4.33.0, so verify version-specific details locally.

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.