Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Clear out junk files and repair common Windows errors3Fix the driver behind crashes, sound loss and screen glitchesUse an XPath text predicate. For an exact table-cell value, start with //table//td[normalize-space(.)='Expected value']. Selenium’s normalize-space(.) trims leading and trailing whitespace and collapses internal runs before comparing, while . includes text rendered by descendants such as nested <span> elements. Scope the XPath to the intended table or row whenever the same value can appear more than once.
The core XPath patterns
Choose the narrowest expression that describes the element you actually need. Use td for data cells, th for header cells, or a more specific table selector when the page contains several tables.
| Need | XPath | What it does |
|---|---|---|
| Exact value anywhere in a table | //table//td[normalize-space(.)='Paid'] |
Matches a data cell whose normalized text is exactly Paid. |
| Exact value in one table | //table[@id='orders']//td[normalize-space(.)='Paid'] |
Restricts the search to the table with id="orders". |
| Text containing a phrase | //table[@id='orders']//td[contains(normalize-space(.), 'Paid')] |
Matches a cell whose normalized text contains the phrase. It can also match longer values such as Unpaid. |
| Find a row, then another cell in that row | //table[@id='orders']//tr[td[normalize-space(.)='Order 123']]//td[normalize-space(.)='Paid'] |
Locates the row identified by one cell and returns the different cell in that same row. |
| Inspect every matching cell | //table//td[normalize-space(.)='Paid'] with findElements |
Returns all candidates so you can check whether the text is unique. |
XPath text comparisons are exact unless you use a function such as contains. They are also sensitive to the actual DOM structure. If the visible label is in a <th>, your //td expression will correctly return no match; change the element test or use a selector that covers both cell types.
Find an exact cell in Java
One expected match
This example scopes the search to the orders table and returns the first matching cell:
#1 Best Overall
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
WebElement cell = driver.findElement(
By.xpath("//table[@id='orders']//td[normalize-space(.)='Paid']")
);
System.out.println(cell.getText());
findElement returns the first element that satisfies the locator. That is convenient when the page contract guarantees one match, but it does not prove that the value is unique.
Find the row by one value and read another cell
For tabular data, identifying a row first is usually safer than searching the whole document for a status. The following expression finds the row containing Order 123 and then selects its Paid cell:
WebElement statusCell = driver.findElement(
By.xpath("//table[@id='orders']//tr[td[normalize-space(.)='Order 123']]//td[normalize-space(.)='Paid']")
);
String status = statusCell.getText();
Replace the second predicate with a column-specific condition if several cells in the row can contain the same text. For example, add a class, data-column attribute, or positional rule only when that structure is stable.
Check uniqueness with plural lookup
import java.util.List;
List<WebElement> matches = driver.findElements(
By.xpath("//table[@id='orders']//td[normalize-space(.)='Paid']")
);
if (matches.size() != 1) {
throw new AssertionError("Expected one Paid cell, found " + matches.size());
}
System.out.println(matches.get(0).getText());
findElements returns every match and returns an empty list when there are none. Use it when duplicate values are possible, when you need to inspect all rows, or when uniqueness itself is part of the assertion.
Find the same element in Python
Exact text and row-scoped lookup
from selenium.webdriver.common.by import By
cell = driver.find_element(
By.XPATH,
"//table[@id='orders']//td[normalize-space(.)='Paid']"
)
print(cell.text)
status_cell = driver.find_element(
By.XPATH,
"//table[@id='orders']//tr[td[normalize-space(.)='Order 123']]//td[normalize-space(.)='Paid']"
)
print(status_cell.text)
The XPath is the same in Python and Java; only the Selenium language binding changes. The Python API exposes the locator strategy through By.XPATH.
Inspect all candidates
matches = driver.find_elements(
By.XPATH,
"//table[@id='orders']//td[normalize-space(.)='Paid']"
)
for match in matches:
print(match.text)
if len(matches) != 1:
raise AssertionError(f"Expected one Paid cell, found {len(matches)}")
Use the rendered .text property when the expected value is what a user sees. An input’s current value, an HTML attribute, and a property supplied at runtime are different data and should be read through the appropriate attribute or property API rather than treated as element text.
Rank #2
How normalize-space(.) affects matching
Whitespace-normalized exact matches
HTML often contains indentation, line breaks, or padding around a label. normalize-space(.) removes leading and trailing whitespace and changes each run of whitespace to a single space before the comparison. Thus a cell rendered across several lines can still match 'Paid' when its meaningful text is exactly that word.
The dot is important. XPath’s . string value includes text from descendant nodes, so a cell such as <td><span>Paid</span></td> can be matched without targeting the nested span separately. The correct expression still depends on the page’s markup; inspect the live DOM if the result is surprising.
Free tools Windows power users keep installed
One-click scans. No signup required.
Exact versus partial text
Use exact matching for statuses, IDs, totals, and other values where a near match is incorrect:
//table[@id='orders']//td[normalize-space(.)='Paid']
Use contains only when partial matching is intentional:
//table[@id='orders']//td[contains(normalize-space(.), 'Paid')]
The latter expression can match Unpaid, Paid in full, or another longer label. If that is not acceptable, use the exact predicate or add a second condition that identifies the intended value.
Scope the search so repeated text is safe
Choose the table first
A page may contain a navigation table, a report table, and a hidden template with the same text. A stable table identifier is preferable:
Recommended Free Tools
Rank #3
//table[@id='orders']//td[normalize-space(.)='Paid']
If there is no ID, scope with another stable attribute or an ancestor relationship. Selenium’s locator guidance recommends a unique, stable ID when one exists; otherwise use a well-written selector appropriate to the element. For a condition based specifically on text and relationships between cells, XPath is the locator strategy that expresses that condition directly.
Keep the expression compact and readable. A long chain of positional steps can break when a column is added, while a table ID plus a text predicate usually states the test’s intent more clearly.
Identify a row by a key cell
When several rows contain the same status, anchor the search on a unique key such as an order number:
//table[@id='orders']//tr[td[normalize-space(.)='Order 123']]//td[normalize-space(.)='Paid']
The first td predicate filters rows; the final predicate selects a cell inside the surviving row. If the key itself can repeat, use plural lookup and assert the expected number of rows before reading a cell.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Wait for dynamic tables before locating text
A correct XPath still fails if the table has not appeared when Selenium searches. Selenium identifies looking too early as a common cause of NoSuchElementException. Wait for the relevant element or condition instead of relying on a fixed sleep.
Java explicit wait
import java.time.Duration;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(15));
WebElement paidCell = wait.until(
ExpectedConditions.visibilityOfElementLocated(
By.xpath("//table[@id='orders']//td[normalize-space(.)='Paid']")
)
);
assert paidCell.getText().equals("Paid");
Python explicit wait
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
paid_cell = WebDriverWait(driver, 15).until(
EC.visibility_of_element_located(
(By.XPATH, "//table[@id='orders']//td[normalize-space(.)='Paid']")
)
)
assert paid_cell.text == "Paid"
Choose the wait condition that matches your test: presence is enough when you only need to inspect the DOM, while visibility is appropriate when the user must see or interact with the cell. Keep the timeout aligned with the application and test environment rather than making every lookup unnecessarily slow.
Rank #4
Read rendered text correctly
Selenium exposes an element’s rendered text through Java’s getText() and Python’s .text. This is the value a user sees after the browser renders the element. It is not interchangeable with:
- An input’s current value, which is held in its value property.
- An HTML attribute such as
aria-label,data-status, ortitle. - Text that exists in the source but is not rendered in the current state.
Use the relevant attribute or property API when the requirement concerns one of those values. Use an XPath text predicate and rendered-text retrieval when the requirement is explicitly about displayed table content.
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 →Diagnose failures systematically
NoSuchElementException
- Confirm that the expression matches the current DOM, not an earlier page version.
- Check whether the target is a
thrather than atd, or whether the table has a different ID or structure. - Verify that the target has appeared before lookup; add an explicit wait for the table or cell.
- Make sure your driver is operating in the correct page context before searching.
These checks address the two common categories Selenium documents for this error: the locator points to the wrong place, or the lookup happens too early.
Invalid selector errors
Malformed XPath syntax, mismatched quotes, or passing an XPath expression under a CSS-selector strategy can produce an invalid-selector error. In Java use By.xpath(...); in Python use By.XPATH. Test the smallest expression first, then add table and row predicates one at a time.
The locator returns an unexpected cell
- Replace a document-wide
//tablesearch with a specific table selector. - Use the row-key pattern when a status or label repeats.
- Switch from
containsto an exactnormalize-space(.)='value'predicate when longer labels are being included. - Call
findElementsand print each candidate’s rendered text to see whether the expression is broader than intended.
The text looks right but the match is empty
Inspect the actual element tags and descendant structure in the browser’s DOM inspector. The visible content may be in a header cell, a nested element, or include whitespace that your original expression did not account for. The . string value plus normalize-space handles many nested-text and spacing cases, but it cannot compensate for selecting the wrong ancestor or table.
Performance, reliability, and maintainability
Keep searches narrow
Searching a specific table and row reduces ambiguity and avoids accidentally matching unrelated content. It also makes failures easier to interpret because the locator describes the business key and the expected cell rather than relying on the first matching text in the document.
Best Value
Prefer stable attributes when text is not the requirement
If the test only needs to identify an element and a unique, stable ID is available, use that ID. If the test must prove what the table displays, retain the XPath text predicate. CSS selectors are useful for selector-based criteria, but CSS does not directly express a condition on an element’s text content.
Separate locating from asserting
First locate the intended cell, then read its rendered text and make the assertion. This gives a clearer failure: a missing element indicates a locator or timing problem, while a different string indicates a page-data or rendering problem.
Use plural lookup when uniqueness is a contract
A singular call silently chooses the first match. A plural call lets the test fail deliberately if a supposedly unique value appears zero times or more than once. That distinction is important for reports where duplicate statuses are normal.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
If your goal is a clean image or PDF of a page rather than an interactive Selenium assertion, ScreenshotNeo can capture the URL through one request. Its pre-capture cleanup accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the page verdict and billing result in X-Page-Verdict and X-Billed headers. It also provides an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchWindows 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 reinstallSee the complete request options in the ScreenshotNeo documentation. A basic 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
Python
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)
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}`);
ScreenshotNeo supports PNG, JPEG, WebP, and PDF output, along with full-page capture, lazy-image loading, CSS-selector element capture, dark mode, device presets, custom viewports, retina scale, PDF paper and page-range controls, custom CSS or JavaScript, pre-capture clicks, selector hiding, selector or network-idle waits, request and resource blocking, custom headers and cookies, user-agent and authorization values, timezone and geolocation, transparent backgrounds, resizing, configurable-TTL caching, signed image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification. Parameter names used by other screenshot APIs also work, which can simplify migration.
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, and yearly billing gives two months free. Create a free ScreenshotNeo account to get started.
Official Selenium references
- Selenium locator strategies explains supported locator types.
- Selenium locator guidance covers stable locator choices.
- Finding web elements documents singular and plural element lookup.
- Information about web elements describes rendered text and element information.
- Selenium common errors covers lookup and selector failures.
- Python’s
ByAPI lists the locator constants, includingBy.XPATH.
Frequently Asked Questions
Do Java and Python use different XPath syntax for table text?
No. The XPath expression is evaluated by the browser, so the text predicate is the same; Java uses By.xpath and Python uses By.XPATH to pass it to Selenium.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →When should a table header be matched instead of a data cell?
Use a th element when the value is a header. A locator restricted to td will not match a header cell, even when the displayed words are identical.
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.

