"""Probe qPublic (Schneider) for Indian River — verify Turnstile is passive, map search flow, extract sample fields.""" from pathlib import Path def probe(): from playwright.sync_api import sync_playwright out_dir = Path(__file__).parent.parent / "_probe_out" / "qpublic" out_dir.mkdir(parents=True, exist_ok=True) with sync_playwright() as p: browser = p.chromium.launch(headless=True) ctx = browser.new_context( user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120", ) page = ctx.new_page() # Capture POST requests captured = [] page.on("request", lambda r: captured.append({"m": r.method, "url": r.url}) if r.method == "POST" else None) url = "https://qpublic.schneidercorp.com/Application.aspx?App=IndianRiverCountyFL&PageType=Search" page.goto(url, wait_until="domcontentloaded") page.wait_for_timeout(3000) print(f"[1] Page loaded: {page.title()}") # Look for disclaimer / accept page (qPublic typically has one on first visit) body_text = page.inner_text("body")[:1500] print(f"\n[2] Body snippet first 1000 chars:\n{body_text[:1000]}") # Check for disclaimer button for txt in ["Accept", "I Accept", "I Agree", "Agree", "Continue", "Disclaimer", "Acknowledge"]: loc = page.locator(f"button:has-text('{txt}'), input[value*='{txt}'], a:has-text('{txt}')") if loc.count() > 0: try: visible = loc.first.is_visible() print(f" Found '{txt}': count={loc.count()} visible={visible}") except Exception: pass # Try clicking Accept/I Agree if exists for txt in ["Agree", "Accept"]: loc = page.locator(f"button:has-text('{txt}'), input[value*='{txt}']").first if loc.count() > 0: try: if loc.is_visible(): loc.click() page.wait_for_timeout(3000) print(f"[3] Clicked '{txt}' button") break except Exception as e: print(f" Click {txt} error: {e}") (out_dir / "01_landing.html").write_text(page.content(), encoding="utf-8") page.screenshot(path=str(out_dir / "01_landing.png"), full_page=True) # Look for search inputs print(f"\n[4] Visible inputs:") for inp in page.locator("input:visible, select:visible").all()[:20]: try: tag = inp.evaluate("el => el.tagName.toLowerCase()") id_ = inp.get_attribute("id") or "" name = inp.get_attribute("name") or "" type_ = inp.get_attribute("type") or "" placeholder = inp.get_attribute("placeholder") or "" if type_ == "hidden": continue # Try to find a label label = "" if id_: lbl = page.locator(f"label[for='{id_}']").first if lbl.count() > 0: label = lbl.inner_text()[:50] print(f" <{tag}> id={id_!r} name={name!r} type={type_!r} placeholder={placeholder!r} label={label!r}") except Exception: pass # Look for tabs/links to switch search modes print(f"\n[5] Tabs / links found:") for el in page.locator("a, button").all()[:30]: try: txt = (el.inner_text() or "").strip()[:60] if any(k in txt.lower() for k in ("owner", "address", "parcel", "search", "by ")): href = el.get_attribute("href") or "" print(f" text={txt!r} href={href[:80]}") except Exception: pass # Look for Turnstile widget instance print(f"\n[6] Turnstile elements on page:") for sel in [".cf-turnstile", "[data-sitekey]", "iframe[src*='turnstile']"]: n = page.locator(sel).count() print(f" {sel}: {n}") # Captured POSTs so far (during landing) print(f"\n[7] POSTs during landing: {len(captured)}") for c in captured: print(f" {c['m']} {c['url'][:100]}") browser.close() if __name__ == "__main__": probe()