What Is Selector Brittleness? β Definition and How It Works
Learn why selectors break, which anchors survive redesigns, and how to build extraction that degrades visibly rather than silently.
Selector brittleness is the tendency of extraction selectors to break when a page changes in ways unrelated to its content. It is the dominant maintenance cost of any collection system.
1. Why Selectors Break
- Redesigns restructure the markup entirely.
- Generated class names change on every build, so
.css-1x7f9k2is valid today and gone tomorrow. - Framework wrappers add or remove nesting levels.
- A/B tests serve different structures to different requests.
- Personalisation varies layout by visitor.
- Optional elements shift positional indexes when absent.
None of these is a content change. All of them break extraction.
2. A Brittleness Ranking
From worst to best:
| Anchor | Durability | Note |
|---|---|---|
Absolute path (div > div > div:nth-child(3)) |
Terrible | One added wrapper breaks it |
Generated class (.css-1x7f9k2) |
Terrible | Changes every build |
| Positional index | Poor | Breaks when an item is absent |
Presentational class (.text-lg) |
Poor | Changes with styling |
Semantic class (.product-title) |
Moderate | Survives styling changes |
data-* attribute ([data-testid="price"]) |
Good | Often used by the site's own tests |
| ARIA role or label | Good | Tied to accessibility, rarely churned |
| Semantic element plus text | Good | Tied to meaning |
| Structured data (JSON-LD) | Best | Maintained for search engines |
The pattern: anchor to meaning, not to appearance or position.
3. Prefer Structured Data First
The most durable extraction avoids selectors altogether. JSON-LD, microdata, and Open Graph tags are maintained because search engines and social platforms consume them, and they survive redesigns that destroy every class name on the page.
An extraction cascade that tries structured data first, falls back to semantic selectors, and only then to positional guesses is dramatically cheaper to maintain than one built on CSS paths.
4. Fallback Chains
Where selectors are unavoidable, try several in order of durability:
PRICE_SELECTORS = [
'[data-testid="product-price"]',
'[itemprop="price"]',
".product-price .amount",
".price",
]
def extract_price(doc):
for sel in PRICE_SELECTORS:
el = doc.css_first(sel)
if el and el.text(strip=True):
return el.text(strip=True), sel
return None, None
Returning the selector that matched is the part worth copying. It tells you, in your own data, when the primary anchor stopped working and a fallback took over β which is early warning rather than an outage.
5. Make Failure Visible
Brittleness is not the real problem. Silent brittleness is.
A broken selector that returns empty produces a record that stores successfully, passes validation if the field is nullable, and quietly fills a column with nulls.
The countermeasures:
- Per-field fill rates, alerted per field, not aggregated.
- Required-field assertions that fail the record rather than nulling it.
- Zero-record page counts as a first-class metric.
- Which-selector-matched tracking, as above.
- A canary page with known expected values, checked every run.
The canary is the cheapest high-value check: one page whose correct output you know, asserted every run.
6. Text Anchors Are Underused
Where a page has a label-value layout, anchoring on the label is far more durable than anchoring on the structure:
# Find the value whose label is "SKU", whatever the markup around it
value = doc.xpath('//*[normalize-space(text())="SKU"]/following::*[1]/text()')
Labels are content and change rarely; the markup wrapping them changes constantly. This is one of the cases where XPath earns its place over CSS.
7. Accepting the Maintenance
Some brittleness is irreducible, so plan for it rather than hoping:
- Version selector definitions in configuration, not scattered through code.
- Keep raw payloads, so a fix can be applied retroactively.
- Keep a fixture of saved HTML per site and test selectors against it.
- Review fill-rate trends on a schedule, not only on alert.
- Expect a redesign per site per year, and budget for it.
Point 2 is what converts a broken selector from lost data into a reprocessing task.
8. Summary
Selector brittleness is extraction breaking on changes unrelated to content β redesigns, generated class names, added wrappers, A/B tests, and absent optional elements.
Anchor to meaning rather than position or styling, prefer structured data before any selector, use ordered fallback chains that record which anchor matched, and make failure visible through per-field fill rates and a canary page β because silent brittleness, not brittleness, is what corrupts a dataset.