Try each selector in turn; return the first that becomes visible. Adapters list multiple plausible selectors per field so a small UI tweak on the target site does not break the run. The first match wins.
(page, selectors: list[str], *, timeout: int = 15000)
| 128 | |
| 129 | |
| 130 | def _find_first(page, selectors: list[str], *, timeout: int = 15000): |
| 131 | """Try each selector in turn; return the first that becomes visible. |
| 132 | |
| 133 | Adapters list multiple plausible selectors per field so a small UI tweak |
| 134 | on the target site does not break the run. The first match wins. |
| 135 | """ |
| 136 | last_error: Exception | None = None |
| 137 | for selector in selectors: |
| 138 | try: |
| 139 | handle = page.wait_for_selector(selector, timeout=timeout, state="visible") |
| 140 | if handle: |
| 141 | return handle |
| 142 | except Exception as err: # noqa: BLE001 — Playwright TimeoutError, etc. |
| 143 | last_error = err |
| 144 | continue |
| 145 | raise AdapterError(f"none of the selectors matched: {selectors}: {last_error}") |
| 146 | |
| 147 | |
| 148 | def _trim_for_meta_description(text: str, limit: int = 140) -> str: |
no test coverage detected