I had a client last year, a massive enterprise equipment supplier, stuck syncing inventory out of a legacy vendor portal that had not been touched since 2004. No API, no JSON endpoints, just nested tables, inline styles, and not one ID or class to grab onto. My junior developer spent three days trying to modernize the scrape with querySelectorAll and long nth-child chains. Every time the vendor dropped in another spacer GIF, the sync broke.
My first instinct was to write a custom regex parser against the raw HTML string. That road goes nowhere, and I nearly burned a weekend proving it to myself: regex is not built to parse a non-regular language like HTML. Then I remembered a piece of the modern browser stack that most younger devs have forgotten about, which is XPath DOM querying.
The precision of XPath DOM querying
React and Vue abstract the DOM so heavily that it is easy to forget what the browser itself can do. CSS selectors are fine for styling, but they are weak for data extraction. They cannot walk back up the tree, and they cannot find an element by the text inside it. XPath does both.
On that inventory project, a 50-line pile of brittle CSS selectors came down to a single XPath expression. Instead of counting table rows, I looked for the cell containing the word “SKU” and took its neighbor.
/**
* Precise data extraction using XPath
* @param {string} bbioon_search_text
* @return {string|null}
*/
function bbioon_fetch_legacy_data(bbioon_search_text) {
const bbioon_xpath = `//td[contains(text(), '${bbioon_search_text}')]/following-sibling::td[1]`;
const bbioon_result = document.evaluate(
bbioon_xpath,
document,
null,
XPathResult.STRING_TYPE,
null
);
return bbioon_result.stringValue ? bbioon_result.stringValue.trim() : null;
}
// Usage: Grab the price next to the 'MSRP' label
const bbioon_price = bbioon_fetch_legacy_data('MSRP');
console.log(bbioon_price);
XPath does not care whether your classes came out of Tailwind, and it survives small shifts in the markup, because it works on the relationship between the data points. That is the argument for keeping the older tools around. XPath, and the now-endangered XSLT, give you a kind of XPath DOM querying precision that div > div > p will never reach.
Why older tech still matters
The WHATWG and Chrome teams are currently debating whether to remove XSLT 1.0. XSLT is a niche interest these days, but the XPath engine underneath it still does real work in automated testing and complex scraping. Sticking to CSS selectors for that job is like doing surgery with a butter knife. You can finish eventually, but it gets messy.
- XPath tests are less likely to flake when your UI framework updates.
- Moving from a child back to a specific parent or sibling is trivial in XPath.
- You can select nodes by the text they contain, not just by their tags, which matters a lot when you are integrating with something legacy.
This stuff gets complicated fast. If you are tired of debugging someone else’s markup and you just want your site to work with the systems you already have, drop my team a line. We have probably seen it before.
So are you still leaning on CSS selectors alone for your automation, or is it time to reach back into the toolbox for something sharper?