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 ChromeOptions with --headless=new, create a ChromeDriver session, navigate, and locate elements with Selenium’s current locator API. In Python, the essential call is driver.find_element(By.ID, "submit"). Reliable scripts also choose stable locators, wait for the condition the next action needs, keep Chrome and ChromeDriver major versions aligned, and call quit() when finished.

Working Python example

The following script starts Chrome without a visible window, opens a page, waits for a button to become clickable, finds it with the current API, and tears down the entire browser session.

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

options = webdriver.ChromeOptions()
options.add_argument("--headless=new")

# Selenium Manager can locate a compatible driver in current Selenium releases.
driver = webdriver.Chrome(options=options)

try:
    driver.get("https://example.com")
    wait = WebDriverWait(driver, 15)
    heading = wait.until(
        EC.visibility_of_element_located((By.TAG_NAME, "h1"))
    )
    print(heading.text)
finally:
    driver.quit()

Install Selenium with python -m pip install selenium. Replace the example URL and locator with the page under test. The explicit wait is intentional: a completed navigation does not guarantee that JavaScript has created or revealed the element you need.

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.

Configure Chrome for headless execution

Use ChromeOptions

Create the binding’s ChromeOptions object, add the browser argument --headless=new, and pass the options to ChromeDriver. Headless is a browser argument, not a different Selenium locator API. Current Selenium Chrome guidance uses this options-based pattern.

from selenium import webdriver

options = webdriver.ChromeOptions()
options.add_argument("--headless=new")
options.add_argument("--window-size=1280,900")
driver = webdriver.Chrome(options=options)

A window-size argument is useful when responsive CSS changes which elements exist or whether they are visible. If Chromium is installed outside the default location, configure the options object with that browser’s binary path according to your binding’s documentation.

Use the right headless switch for your environment

Selenium’s older convenience headless method was removed in Selenium 4.10.0 so users could select a mode. Current Chrome-specific guidance identifies --headless=new. If an older Selenium or Chrome installation rejects the argument, check the versions actually installed and use the option documented for that combination rather than copying an obsolete helper call.

Find an element with the current API

Python syntax

Import By and pass a strategy plus locator value:

from selenium.webdriver.common.by import By

submit = driver.find_element(By.ID, "submit")
submit.click()

The old helpers such as find_element_by_id are not the current Python interface. find_element returns the first matching element and raises an error when no match exists. Use find_elements when zero matches are a valid result; it returns a list, which may be empty.

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

Supported strategies

Strategy Example When to use it
ID By.ID, "submit" A stable unique id is usually the clearest choice.
Name By.NAME, "email" Useful for stable form controls.
CSS selector By.CSS_SELECTOR, "[data-test='submit']" Prefer a dedicated, stable attribute such as data-test.
XPath By.XPATH, "//button[@type='submit']" Helpful when a relationship or text condition cannot be expressed simply in CSS.
Class name By.CLASS_NAME, "primary" Only when the class is an intentional stable hook.
Tag name By.TAG_NAME, "h1" Useful when the page has a single meaningful tag.
Link text By.LINK_TEXT, "Documentation" For an exact anchor label.
Partial link text By.PARTIAL_LINK_TEXT, "Doc" For a deliberately partial anchor label.

Prefer a stable ID or name, then a CSS selector using a dedicated attribute. Absolute XPath (for example, a full /html/body/... path) and generated class names are brittle because small markup or build changes invalidate them. Relative locators are also available in current Selenium bindings when spatial relationships are the most useful description.

Wait for the state your next command needs

Why navigation is not enough

Navigation waits according to the session’s page-load strategy, but that readiness covers document resources. Client-side code can still insert, replace, or reveal the target afterward. A returned get() call therefore does not prove that an element is present or interactable.

Explicit waits

Choose a condition that matches the next operation:

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

wait = WebDriverWait(driver, 20)

# Exists in the DOM:
card = wait.until(EC.presence_of_element_located(
    (By.CSS_SELECTOR, "[data-test='result']")
))

# Has been rendered and visible:
search = wait.until(EC.visibility_of_element_located(
    (By.NAME, "q")
))

# Can receive the interaction:
button = wait.until(EC.element_to_be_clickable(
    (By.ID, "submit")
))
button.click()

Use a delay only when you have no more precise condition. Waiting for a selector or state makes failures faster and explains what was missing.

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

Implicit, explicit, and page-load waits

A new session’s implicit element-location timeout defaults to zero. Do not combine implicit and explicit waits in the same session: their polling behavior can compound timeouts and make failures difficult to predict.

Selenium provides three page-load strategies:

Strategy Navigation waits for Trade-off
normal The load event and associated resources Most waiting before the call returns; JavaScript may still change the page.
eager DOMContentLoaded Returns earlier, so subsequent explicit waits must cover more application work.
none The initial page download Fastest return, but every required condition must be waited for explicitly.
options = webdriver.ChromeOptions()
options.add_argument("--headless=new")
options.page_load_strategy = "eager"
driver = webdriver.Chrome(options=options)

Changing this setting affects the whole session. Pick the strategy for your application and retain explicit waits for the actual state each test needs.

Diagnose “no such element” in headless Chrome

  1. Confirm the active page. Print driver.current_url and inspect the title or saved page source. A redirect, authentication page, or error document may be active instead of the intended URL.
  2. Check the markup and locator. Verify the ID, name, attribute, or text in the current DOM. Replace generated classes and absolute XPath with a stable hook where possible.
  3. Check timing. If a client-side framework creates the element after navigation, add an explicit wait for presence, visibility, or clickability.
  4. Check the browsing context. If the element belongs to a frame, the intended frame must be active before lookup; inspect the page structure and switch context using your binding’s documented frame API.
  5. Check headless-specific layout. Set a representative window size and verify that responsive rules have not hidden or replaced the control.
  6. Check the browser session itself. If ChromeDriver cannot create a session, investigate browser and driver compatibility before changing locators.

Common startup errors

Symptom Likely cause Fix
Session fails before the first page loads Chrome and ChromeDriver major versions do not match, or the browser binary is not where the driver expects. Check both installed versions, align their major versions, and set the binary location when using a non-default Chromium installation.
find_element raises immediately The element is absent at lookup time. Verify the current URL and DOM, then wait for the required condition.
Element exists but click fails It is present but not visible or interactable. Wait for visibility or clickability and check overlays or responsive layout.
Intermittent failures A race between navigation or JavaScript rendering and the lookup. Use one consistent explicit-wait strategy; remove arbitrary sleeps and do not mix implicit waits.

Version and teardown checklist

  • Use Selenium 4-compatible code with a ChromeOptions object.
  • Pass --headless=new as a Chrome argument.
  • Keep Chrome and ChromeDriver major versions aligned; Selenium’s Chrome documentation describes Selenium 4 compatibility with Chrome v75 and later.
  • Use find_element(By.<strategy>, locator), not removed Python convenience methods.
  • Choose stable IDs, names, or test attributes.
  • Wait for the condition required by the next command.
  • Call driver.quit() in cleanup; close() only closes a window and is not the recommended session teardown.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If your goal is a clean screenshot rather than browser interaction, ScreenshotNeo provides a single HTTP request. It accepts cookie and consent banners as a visitor, then removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each cleanup step can be disabled. Only clean shots are billed: bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and the response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.

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 all options, including PNG, JPEG, WebP, PDF, full-page capture, custom waits, selectors, headers, cookies, device presets, and asynchronous jobs.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

The Free plan includes 1,000 screenshots each month with no card. Paid plans start at $5 for 3,000 shots; every feature is available on every plan. Create a free ScreenshotNeo account.

FAQ

What does find_elements return when nothing matches?

It returns an empty list. Use it when zero matches is expected; use find_element when one match is required and a missing element should fail the operation.

Can I use headless mode with a non-default Chromium browser?

Yes, when your Selenium binding supports a browser binary option. Set the ChromeOptions binary location to the installed executable and verify that the corresponding driver major version is compatible.

Which wait should I use for a button?

Wait for clickability when the next operation is a click. Presence only proves that an element is in the DOM; visibility proves it is rendered, not necessarily ready to receive the interaction.

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

Frequently Asked Questions

What does find_elements return when nothing matches?

It returns an empty list; use find_element when a missing match should raise an error.

Can headless Selenium run with a non-default Chromium binary?

Yes. Set the browser binary through ChromeOptions and keep the browser and driver major versions compatible.

Which wait is appropriate before clicking a button?

Use an explicit wait for clickability; presence alone only confirms that the element is in the DOM.

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.

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