add criteria-first discovery search provider
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
"""Bounded, public-only prospect discovery using an explicit seed allowlist.
|
||||
"""Bounded, public-only prospect discovery with optional criteria search.
|
||||
|
||||
There is deliberately no general web search or arbitrary URL input here: callers provide
|
||||
at most a small set of public seed pages. Only links found on those seeds become candidate
|
||||
sites, and subsequent crawling is same-origin, bounded, and SSRF-checked by the scanner.
|
||||
at most a small set of public seed pages. When seeds are omitted, a configured search
|
||||
provider supplies bounded seed URLs. Links found on those seeds become candidate sites,
|
||||
and subsequent crawling is same-origin, bounded, and SSRF-checked by the scanner.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -12,6 +13,7 @@ from urllib.parse import urljoin, urldefrag, urlparse
|
||||
|
||||
from .contact_extractor import extract_contacts
|
||||
from .website_scanner import MAX_BYTES, MAX_REDIRECTS, _fetch, validate_url
|
||||
from .search_provider import search as search_provider
|
||||
|
||||
MAX_SEEDS = 5
|
||||
MAX_PAGES = 20
|
||||
@@ -61,14 +63,21 @@ def _criteria_match(text, criteria):
|
||||
return not keywords or all(str(k).strip().lower() in haystack for k in keywords if str(k).strip())
|
||||
|
||||
|
||||
def discover(criteria, seed_urls, *, max_pages=MAX_PAGES, max_candidates=MAX_CANDIDATES):
|
||||
def discover(criteria, seed_urls=None, *, max_pages=MAX_PAGES, max_candidates=MAX_CANDIDATES):
|
||||
if not isinstance(criteria, dict) or len(criteria) > 20: raise ValueError("invalid_criteria")
|
||||
if not isinstance(seed_urls, list) or not 0 < len(seed_urls) <= MAX_SEEDS: raise ValueError("seed_urls_required")
|
||||
try: max_pages = int(max_pages); max_candidates = int(max_candidates)
|
||||
except (TypeError, ValueError): raise ValueError("invalid_limits")
|
||||
if not 1 <= max_pages <= MAX_PAGES or not 1 <= max_candidates <= MAX_CANDIDATES: raise ValueError("invalid_limits")
|
||||
if seed_urls is None:
|
||||
seeds = search_provider(criteria, max_candidates)
|
||||
mechanism = "criteria_search_provider"
|
||||
else:
|
||||
if not isinstance(seed_urls, list) or not 0 < len(seed_urls) <= MAX_SEEDS: raise ValueError("seed_urls_required")
|
||||
seeds = list(seed_urls)
|
||||
mechanism = "explicit_seed_allowlist"
|
||||
raw_seeds = list(seeds)
|
||||
seeds = []
|
||||
for raw in seed_urls:
|
||||
for raw in raw_seeds:
|
||||
try: safe = validate_url(raw)
|
||||
except ValueError as exc: raise ValueError("unsafe_seed_url") from exc
|
||||
if safe not in seeds: seeds.append(safe)
|
||||
@@ -125,5 +134,5 @@ def discover(criteria, seed_urls, *, max_pages=MAX_PAGES, max_candidates=MAX_CAN
|
||||
evidence.append({"kind": "discovery_page", "url": page["url"], "claim": claim, "provenance": "scoped_discovery"})
|
||||
deduped = {(x["kind"], x["value"]): x for x in contacts}
|
||||
name = next((x["headings"][0] for x in pages if x["headings"]), next((x["title"] for x in pages if x["title"]), domain))
|
||||
results.append({"name": name[:200], "website": root, "website_domain": domain, "description": text[:1000], "contacts": list(deduped.values())[:100], "evidence": evidence, "pages": pages, "pages_crawled": len(pages), "provenance": {"mechanism": "explicit_seed_allowlist", "seed_urls": seeds, "root_url": root}})
|
||||
results.append({"name": name[:200], "website": root, "website_domain": domain, "description": text[:1000], "contacts": list(deduped.values())[:100], "evidence": evidence, "pages": pages, "pages_crawled": len(pages), "provenance": {"mechanism": mechanism, "seed_urls": seeds, "root_url": root}})
|
||||
return {"candidates": results, "seeds": seeds, "pages_limit": max_pages, "candidate_limit": max_candidates}
|
||||
|
||||
Reference in New Issue
Block a user