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

For an HTML element with a known ID, use //*[@id='element-id']. It tests the literal id attribute and works in HTML and in many automation contexts. XPath also defines id('element-id'), but that function works only when the XPath processor knows which attribute is typed as an ID. In Selenium, use By.ID for a straightforward lookup and By.XPATH when you need XPath predicates, relationships, or text conditions.

The three XPath forms you need

These expressions can all refer to an element whose ID is login, but they do not have identical portability or behavior.

Expression What it tests Best use Important limitation
id('login') The XPath processor’s ID index Documents and processors with reliable ID typing Returns nothing when the processor does not know that the relevant attribute is an ID
//*[@id='login'] The literal id attribute on any element HTML automation and scraping where portability matters Can match more than one node if the document contains duplicate IDs
//input[@id='login'] An input element whose literal ID is login When the element type is part of the requirement It will not match the same ID on another element type

For most HTML pages, start with //*[@id='login'], or qualify it with the expected element name. Use id() only when you know the document and XPath implementation expose the attribute as an ID.

How the XPath id() function works

id() is a function, not a shorthand for an attribute predicate. In XPath 1.0, the document’s type information determines which attributes are IDs. A DTD can declare an attribute as type ID, and XML vocabularies can define an ID attribute whose name is not literally id. If the processor has no such typing information, id('login') may return an empty node-set even though an element visibly has id='login'.

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

That distinction explains many “XPath id() does not find my HTML element” reports. HTML exposes an id attribute, but an XPath engine is not required to treat every attribute named id as a typed XML ID in every context. The explicit form, //*[@id='login'], asks the engine to test the attribute itself and therefore avoids that dependency.

HTML IDs, XML IDs, case, and uniqueness

HTML

HTML ID values are case-sensitive: login and Login are different values. An ID is intended to be unique in the document. Valid markup should contain one element for a given value, but real pages sometimes repeat IDs because of templates, duplicated components, or invalid generated markup.

XML

XML applications determine ID typing through their document language and declarations. The attribute may have a different name, and an XPath implementation that lacks the relevant type information cannot resolve it through id(). When the typing rules are unknown, use an explicit attribute test such as //*[@data-key='login'] for that vocabulary, or the literal id predicate when that is the attribute you need.

Duplicate IDs

//*[@id='login'] returns every matching node. A DOM convenience method such as getElementById() returns the first match, so it can conceal a duplicate that an XPath query exposes. If your test requires one element, validate uniqueness rather than silently accepting the first result.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #2
XPath 2.0 Programmer's Reference
  • Used Book in Good Condition

Selecting an ID in Selenium

Selenium provides separate locator strategies for IDs and XPath. Choose the direct ID strategy when the value is known and no additional logic is required:

from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get('https://example.com')

login = driver.find_element(By.ID, 'login')
login.click()

driver.quit()

The equivalent XPath locator is:

from selenium.webdriver.common.by import By

element = driver.find_element(By.XPATH, "//*[@id='login']")

Selenium’s JavaScript By.id implementation uses a CSS selector of the form *[id="$ID"], while By.xpath evaluates the XPath expression. That distinction is normally invisible to your test, but it is useful when diagnosing a locator that behaves differently from a hand-written XPath.

When XPath is better than a direct ID lookup

A direct ID lookup is the simplest choice when the ID is stable. XPath becomes useful when the ID is only one part of the condition.

Restrict the element type

//input[@id='login']
//button[@id='login']

This protects a test from matching an unexpected element that happens to reuse the same ID.

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.

Use an ancestor or descendant relationship

//form[@id='account']//input[@name='email']
//*[@id='settings']//button[@type='submit']

These expressions first anchor the search to a stable ID, then express the required relationship. They are more resilient than an absolute path such as /html/body/div[2]/form/input, which depends on the exact layout and position of every ancestor.

Add a text or attribute predicate

//*[@id='account']//button[contains(normalize-space(.), 'Continue')]
//*[@id='results']//a[@aria-current='page']

Use predicates only for conditions that are part of the requirement. If the ID alone identifies the target, adding unnecessary text or position tests makes the locator harder to maintain.

Building XPath values safely at runtime

Host-language quoting can change the XPath before the browser or parser evaluates it. Keep the XPath string separate from the data used to build it, and escape a value according to both the host language and XPath rules. For a fixed value, this Python expression is unambiguous:

element_id = 'login'
xpath = "//*[@id=" + "'" + element_id + "'" + "]"
element = driver.find_element(By.XPATH, xpath)

If an externally supplied ID can contain quote characters, do not concatenate it blindly. Construct an XPath string literal with the XPath concat() function when necessary, or use the locator API’s direct ID strategy when you do not need XPath logic. The important check is that the final XPath sent to Selenium is the expression you intended, not a string altered by host-language quoting.

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

A practical selection workflow

  1. Confirm the exact value and case. Inspect the element and copy the ID exactly; login is not Login.
  2. Check uniqueness. Search the document for the same ID. If multiple nodes exist, decide which element type or ancestor should qualify the XPath.
  3. Start with the simplest locator. In Selenium, use By.ID for a plain ID lookup. In a general XPath context, use //*[@id='value'] when ID typing is uncertain.
  4. Add only necessary structure. Add an element name, ancestor, descendant, or predicate when the requirement demands it.
  5. Test the expression in the same environment as the automation. Different XPath processors can expose different ID-typing information, so a query that works in one tool is not proof that id() will work everywhere.

Troubleshooting ID-based XPath

id('x') returns no result

The processor probably does not know that the relevant attribute is typed as an ID. Replace it with //*[@id='x'], or use an element-qualified form such as //input[@id='x'].

The expression matches nothing, but the element is visible

Check spelling and case first. ID values are case-sensitive. Then verify that the automation is evaluating the XPath in the document or frame that actually contains the element.

The expression returns several nodes

The page contains duplicate IDs, or your predicate is broader than intended. Qualify the element name or anchor the query to a stable ancestor. Do not rely on “the first” result unless that behavior is explicitly acceptable.

An absolute XPath broke after a redesign

Paths beginning with /html/body and positional steps such as div[2] encode layout details. Replace them with a relative expression anchored to a stable ID and, where needed, a semantic relationship.

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

Selenium rejects the locator string

Inspect the host-language quoting. A quote intended for the XPath may have ended the Python, JavaScript, or other string early. Log the final XPath value and test that exact value in the same browser session.

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

Performance and reliability considerations

There is no useful universal speed number for these expressions: the result depends on the browser, XPath engine, document size, and page state. Reliability matters more than micro-optimizing a locator. A direct ID lookup communicates intent clearly; an XPath predicate is justified when it expresses a real relationship or disambiguates duplicate markup.

Prefer stable, semantic anchors over generated class names and absolute positions. If a page’s framework generates a different ID on every render, an ID-based locator is inherently unstable; identify a durable ancestor, attribute, or relationship instead. Conversely, if the ID is stable and unique, adding extra axes and predicates creates more places for a future markup change to break the test.

Or skip the browser setup

If your goal is to obtain a rendered image of a page for documentation, review, or an automated workflow rather than interact with it, ScreenshotNeo can return a screenshot or PDF through one request. Its CSS-selector element capture can target a rendered element, while XPath remains the right choice for XPath-based browser interaction. See the ScreenshotNeo API documentation for parameters and response details.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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}`);

Before capture, ScreenshotNeo accepts the cookie or consent banner like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and whether the request was billed. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. Sign up for the free plan.

Frequently Asked Questions

Can id() identify more than one element?

Yes. The function can resolve one or more IDs when the processor has ID-typing information. If a document violates the uniqueness convention, inspect all returned nodes rather than assuming one target.

Is //*[@id='x'] limited to HTML?

No. It tests an attribute named id, so it can be used in XML or other documents that use that attribute name. It does not, however, automatically find an XML vocabulary’s differently named ID attribute.

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.