When a Selenium test cannot find the element it needs, the rest of the flow usually stops there. I have seen simple changes to an ID, class name, or page structure turn a working test into a failure even though the feature itself was still fine.
That is why element location is one of the first things I pay attention to when writing Selenium tests in Python. Choosing the right locator makes the test easier to read, less fragile, and much easier to maintain when the UI changes.
In this guide, I look at how to find elements in Selenium with Python using ID, name, XPath, CSS selectors, class names, and other locator strategies, along with when to use find_element() and find_elements() in real test scenarios.
What are Elements in Selenium?
In Selenium, I work with web elements every time I interact with a page. These are the HTML components users see or use, such as buttons, text fields, checkboxes, dropdowns, links, and other page content.
Selenium represents these elements through the WebElement interface. Once I locate an element, I can perform actions such as clicking it, entering text, reading its value, or checking its state.
To find the right element, I use locator strategies such as ID, name, class name, XPath, or CSS selectors. The better the locator, the easier it is to keep the test stable when the page changes.
Also Read: Selenium WebElement Commands
Why Web Elements Matter in Automation
Web elements are at the center of most automation and testing scripts because they are how I tell Selenium what to interact with on a page. If I cannot identify the right element reliably, even a simple test can break before it gets to the actual validation.
- Interact with the page: Web elements let me click buttons, type into input fields, select options, open menus, and perform the same actions a user would.
- Verify application behavior: I can read text, inspect attributes, check whether an element is displayed or enabled, and confirm that the page responds correctly after an action.
- Build complete user flows: Locating individual elements makes it possible to automate end-to-end journeys such as login, search, checkout, form submission, and account creation.
- Handle dynamic content: Modern pages often load or update elements after the initial page load. By locating those elements correctly and pairing them with waits, I can make tests more reliable even when content appears asynchronously.
- Reduce flaky failures: Stable locators help prevent tests from breaking because of small UI changes. I usually prefer selectors that are specific enough to identify the element without depending too heavily on fragile page structure.
- Reuse test logic: Once elements are identified consistently, I can organize them into reusable page objects or helper methods, which makes larger test suites easier to maintain.
- Improve debugging: When a test fails, a clear element locator makes it easier to understand whether the problem came from the page, the locator, or the test logic itself.
How Locators Identify Web Elements
Element locators are the selectors I use to tell Selenium which part of the page I want to work with. A locator can point to a single element, such as a login button, or a group of elements, such as every product card on a page.
Selenium supports several locator strategies, and I choose between them based on how the page is structured and which attributes are likely to remain stable.
| Locator Type | What I use it for |
|---|---|
| ID | Finds an element by its id attribute. I usually prefer this when the ID is unique and stable. |
| Name | Finds elements using the name attribute, which is common in forms and input fields. |
| XPath | Locates elements using XPath expressions. It is useful when I need to navigate relationships between elements or target something without a simple unique attribute. |
| Link Text | Finds a link using its exact visible text. |
| Partial Link Text | Finds a link using part of its visible text when the full label may vary. |
| Tag Name | Finds elements by HTML tag, such as input, button, or a. This is useful when I expect multiple matches. |
| Class Name | Finds elements using a CSS class. I use this carefully because classes are often shared by many elements. |
| CSS Selector | Uses CSS selector syntax to target elements by attributes, classes, IDs, or combinations of these. It is one of the most flexible options for web automation. |
For example:
driver.find_element(By.ID, "login-button")
This returns the first element with the ID login-button.
driver.find_elements(By.NAME, "username")
This returns a list of all elements whose name attribute is username.
driver.find_elements(By.XPATH, "//input")
This finds all <input> elements on the page.
driver.find_elements(By.CSS_SELECTOR, "p.content")
This finds all paragraph elements with the content class.
The important part is not simply knowing every locator type. I try to choose a locator that is specific, readable, and unlikely to change when the UI is updated. That usually makes the test easier to maintain and less likely to fail for reasons unrelated to the feature itself.
Read More: Locators in Selenium: A Detailed Guide
Getting Python Ready for Web Automation
Before I can locate elements, I first need Selenium available in the Python environment. The setup is small and only takes a couple of steps.
1. Install Selenium
Use pip to install the Selenium package:
pip install selenium
With modern Selenium versions, Selenium Manager can handle driver management automatically in many common setups, so I usually do not need to download and configure a browser driver manually.
2. Import the modules
Next, import WebDriver and the By class, which I will use for locator strategies:
from selenium import webdriver from selenium.webdriver.common.by import By
At this point, I am ready to launch a browser and start finding elements with locators such as ID, CSS selector, XPath, or name.
Ways To Locate Elements in Python
Once the browser is running, I choose a locator based on what is most stable on the page. I usually start with a unique ID or a clear CSS selector, then move to XPath when I need more control over relationships or text.
Before the examples, there is one distinction I keep in mind:
- find_element() returns the first matching element and raises an exception if nothing matches.
- find_elements() returns all matching elements as a list. If nothing matches, it returns an empty list.
Read More: findElement and findElements in Selenium
1. Locate an Element by ID
If an element has a unique and stable id, this is usually the first locator I try because it is simple and easy to read.
element = driver.find_element(By.ID, "element_id")
For example:
login_button = driver.find_element(By.ID, "login-button")
Because an HTML id is expected to be unique on the page, I would normally use find_element() rather than find_elements() with this locator.
2. Locate an Element with XPath
I use XPath when the element does not have a useful ID or when I need to locate it by text, attributes, or its relationship to another element.
# Find by exact text element = driver.find_element( By.XPATH, "//*[text()='Get started free']" ) # Find by partial text element = driver.find_element( By.XPATH, "//*[contains(text(), 'Get started')]" ) # Find by attribute element = driver.find_element( By.XPATH, "//input[@name='username']" )
XPath is flexible, but I try to keep expressions short and avoid selectors that depend heavily on the page hierarchy.
3. Locate an Element with a CSS Selector
CSS selectors are another option I use often because they can target IDs, classes, attributes, and combinations of these without much syntax.
# By class element = driver.find_element( By.CSS_SELECTOR, ".classname" ) # By ID element = driver.find_element( By.CSS_SELECTOR, "#elementid" ) # By attribute element = driver.find_element( By.CSS_SELECTOR, "input[name='username']" )
When the page already has clear CSS-friendly attributes, this is often one of the easiest locators to maintain.
4. Locate Links by Their Text
For anchor elements, Selenium lets me use the visible link text directly.
# Exact link text link = driver.find_element( By.LINK_TEXT, "Help Center" ) # Partial link text partial_link = driver.find_element( By.PARTIAL_LINK_TEXT, "Help" )
I use LINK_TEXT when the label is stable. PARTIAL_LINK_TEXT is useful when only part of the text is predictable.
5. Use Name, Tag Name, or Class Name
These locators are useful when the page structure makes them a better fit:
| Locator Method | When I use it | Example |
|---|---|---|
| Name | When a form field has a stable name attribute | driver.find_element(By.NAME, “username”) |
| Tag Name | When I want to target elements by HTML tag | driver.find_elements(By.TAG_NAME, “input”) |
| Class Name | When an element has a useful single CSS class | driver.find_element(By.CLASS_NAME, “login-button”) |
In practice, I do not choose a locator just because Selenium supports it. I look for the option that is specific enough to find the right element and stable enough to survive normal UI changes.
How to Keep Element Locators Reliable
A locator only works well if it keeps finding the right element as the page changes. I usually focus more on stability and readability than on making a selector as short or clever as possible.
- Prefer unique and stable attributes: I start with a unique id, name, or dedicated test attribute when one is available. These are usually less likely to break than selectors tied to the page layout.
- Keep selectors specific: CSS selectors and IDs are often clearer than broad locators such as tag names. For example, targeting input[name=”email”] is safer than locating every input element and relying on its position.
- Avoid brittle XPath expressions: I try not to use long absolute XPath expressions such as /html/body/div[2]/… because small DOM changes can break them. Shorter XPath expressions based on stable attributes or relationships are easier to maintain.
- Use explicit waits for dynamic elements: I do not assume an element is ready just because the page has loaded. Explicit waits let me wait for the exact condition I need before interacting with it.
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC element = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, "element_id")) )
- Use relative locators when relationships are clearer: Selenium 4 supports relative locators such as above(), below(), and near(). I use these when an element is easier to describe in relation to another stable element than through its own attributes.
- Do not depend on generated values: Dynamic IDs, changing class names, and index-based selectors can make tests fragile. If those values change between builds, I look for a more predictable attribute.
- Choose maintainability over tiny speed differences: Locator performance rarely matters as much as whether the selector remains reliable over time. I would rather use a clear, stable locator than optimize for a negligible lookup-time difference.
The best locator is usually the one another tester can understand quickly and that still works after a normal UI update.
Conclusion
Finding elements is one of the most basic parts of Selenium automation, but it has a big effect on how reliable my tests become.
I try to choose locators that are clear, stable, and easy to maintain instead of relying on selectors that break whenever the page structure changes.
I also avoid treating locators in isolation. Combining the right selector with explicit waits and sensible element handling makes my tests much less flaky. Once those basics are in place, the rest of the Selenium workflow becomes easier to build, debug, and maintain.



