mirror of
https://github.com/karust/openserp.git
synced 2026-08-15 21:15:56 +08:00
feat(cli): clean search, add extract/format flags; harden engines & proxy rotation; fix bugs; update docs
- Add structured `search [engine] [query]` CLI: --limit/--lang/--region/--site/--file, --format (json|text|markdown|ndjson), --extract N, --search-timeout; Envelope and route logs to stderr with a --quiet default (fixes stdout pollution) - Unify engines behind a single engineSpec registry (CLI + serve share it) - Unify the extract knob to bool-or-int `extract=N` (drop extract_top); CLI and HTTP share core batch extraction, raw/rendered fetch, and clamp helpers - Engines: Ecosia CF captcha detection (raw + browser), Yandex progressive-result wait, Google PAA poll + Has() existence probes, Bing title/desc attribute fallbacks - Proxy: rotate challenged proxies out of the tag pool for one retry (X-Proxy-Attempts); browser health-ping skip window; opt-in WaitStable
This commit is contained in:
@@ -42,6 +42,12 @@ func (gogl *Google) getTotalResults(page *rod.Page) (int, error) {
|
||||
return 0, core.ErrParser
|
||||
}
|
||||
|
||||
// Stats div is absent on many locales; probe first so .Search doesn't block
|
||||
// the full selector timeout.
|
||||
if has, _, err := page.Has(Selectors.ResultStats); err != nil || !has {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
resultsStats, err := page.Timeout(gogl.GetSelectorTimeout()).Search(Selectors.ResultStats)
|
||||
if err != nil {
|
||||
return 0, errors.New("Result stats not found: " + err.Error())
|
||||
@@ -147,7 +153,33 @@ func (gogl *Google) preparePage(page *rod.Page) {
|
||||
}
|
||||
}
|
||||
|
||||
// waitAnswersExpanded polls until the first PAA entry has expanded to a
|
||||
// title+body (≥2 text lines) or maxWait elapses, replacing a flat 2s sleep.
|
||||
func (gogl *Google) waitAnswersExpanded(ctx context.Context, answers rod.Elements, maxWait time.Duration) error {
|
||||
if len(answers) == 0 || maxWait <= 0 {
|
||||
return nil
|
||||
}
|
||||
deadline := time.Now().Add(maxWait)
|
||||
for {
|
||||
text, err := answers[0].Text()
|
||||
if err == nil && len(strings.Split(text, "\n")) >= 2 {
|
||||
return nil
|
||||
}
|
||||
if !time.Now().Before(deadline) {
|
||||
return nil
|
||||
}
|
||||
if err := core.SleepContext(ctx, 100*time.Millisecond); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (gogl *Google) acceptCookies(page *rod.Page) {
|
||||
// Probe with Has first so a banner-less SERP doesn't block on .Search's full
|
||||
// timeout (AGENTS.md: use Has for existence).
|
||||
if has, _, err := page.Has(Selectors.CookieBtn); err != nil || !has {
|
||||
return
|
||||
}
|
||||
diaglogBtns, err := page.Timeout(gogl.Timeout / 10).Search(Selectors.CookieBtn)
|
||||
if err != nil {
|
||||
gogl.logger.Debug("Cookie consent not found: %s", err)
|
||||
@@ -331,7 +363,8 @@ func (gogl *Google) Search(ctx context.Context, query core.Query) (results []cor
|
||||
}
|
||||
|
||||
}
|
||||
if err := core.SleepContext(ctx, 2*time.Second); err != nil {
|
||||
// Poll for expansion (usually 200-400ms) instead of a flat 2s sleep.
|
||||
if err := gogl.waitAnswersExpanded(ctx, answers, 2*time.Second); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user