Locators: XPath¶
What was run¶
Real XPath expressions exercising exactly the capabilities Lesson 4's CSS selectors structurally cannot provide: matching on visible text content, and traversing upward or sideways through the real DOM tree from a text match — each verified against real elements, including one real, honest off-by-one mistake caught and fixed mid-investigation.
Real text-based matching — no CSS equivalent exists¶
el = driver.find_element(By.XPATH, "//a[text()='Login']")
el = driver.find_element(By.XPATH, "//a[contains(text(), 'Window')]")
REAL XPath exact text match: href=http://127.0.0.1:5050/login
real XPath contains(text()) match: href=http://127.0.0.1:5050/windows
Standard CSS has no selector for "an element whose visible text equals/contains X" — this is real, genuine XPath-only territory.
Real upward traversal — structurally impossible in standard CSS¶
A real, direct demonstration of something CSS selectors cannot do at all: starting from a matched child
(<td>Item5</td>) and climbing up to its real parent <tr>. CSS selectors only ever describe a target
relative to its ancestors or preceding siblings — never the reverse.
A real, honest mistake, caught and fixed¶
selenium.common.exceptions.NoSuchElementException: ... selector":"//td[text()='Item7']/following-sibling::td[2]"
This real query failed — investigated directly: Item7 lives in the second column (name-cell), so it has
only one real following sibling (price-cell), not two. following-sibling::td[2] was asking for a
second sibling that doesn't exist. The real, corrected fix:
A real, direct equivalence check where both languages CAN express the same query¶
css_el = driver.find_element(By.CSS_SELECTOR, "tbody tr:nth-child(5) td.price-cell")
xpath_el = driver.find_element(By.XPATH, "//tbody/tr[5]/td[3]")
How It Actually Works¶
XPath models the real HTML document as a navigable tree with explicit axes — parent::, ancestor::,
following-sibling::, preceding-sibling::, and more — that can move in any real direction from a starting
point, including backward and upward. CSS selectors only describe a target element's relationship to things
that come before it in the tree (ancestors, preceding siblings) — there's no standard CSS way to select "the
parent of the thing I just matched," which is exactly the real gap this lesson's parent:: example fills.
This lesson's own real mistake (following-sibling::td[2] instead of td[1]) is worth sitting with: XPath's
sibling-counting axes count only in the specified direction, starting fresh from 1 — they don't use the
element's absolute position in its parent, which is a real, common source of off-by-one errors until you've
made (and fixed) one yourself, as this lesson's script just demonstrated directly.
Common mistakes¶
- Reaching for XPath's
parent::/ancestor::/following-sibling::axes when a simple CSS selector would already do the job — this lesson's final equivalence check shows CSS and XPath landing on the identical real result when the query doesn't actually need XPath's extra directional power. - Miscounting sibling position with
following-sibling::orpreceding-sibling::— this lesson's own real, caught mistake is a direct, concrete example of exactly this error, and how to actually debug it (check what real siblings exist relative to the match, not just guess at an index). - Writing XPath expressions that start from the document root (
/html/body/...) instead of relative text or attribute matches — those are fragile against any structural change anywhere above the real target; this lesson's//td[text()='Item7']pattern is far more robust because it anchors to real, meaningful content.
Exercise¶
Using only the preceding-sibling:: axis (the mirror image of this lesson's following-sibling::), write a
real XPath expression that starts from the <td class="price-cell"> containing '30' and finds the real
<td class="name-cell"> in the same row. Verify it returns 'Item3', and confirm you get the real index
right on the first try this time.