fish2018/pansou · error

[ ] 解析搜索结果失败

Error message

[%s] 解析搜索结果失败: %w

What it means

After fetching and size-checking the body, searchImpl hands the raw HTML to parseListItems to extract result entries. This error wraps any failure from that parser — the HTML structure did not match what the parser expects, so no valid result list could be produced. It signals an upstream page-shape mismatch, not a network problem.

Solutions

  1. Dump a sample of body on failure and compare the actual DOM against the selectors parseListItems expects; update the parser to the new structure.
  2. Check whether the response is an anti-bot/CAPTCHA or login page (search for telltale markers in body) and handle that as a distinct case instead of a parse failure.
  3. Pin or update the plugin version to one matching the current site layout; check upstream for an existing fix.
  4. Return an empty result set instead of an error when the page parses but contains a recognized 'no results' template, so legit empty searches don't surface as failures.

Example fix

// before
items, err := parseListItems(body)
if err != nil {
    return nil, fmt.Errorf("[%s] 解析搜索结果失败: %w", p.Name(), err)
}

// after
items, err := parseListItems(body)
if err != nil {
    if bytes.Contains(body, []byte("captcha")) || bytes.Contains(body, []byte("verify")) {
        return nil, fmt.Errorf("[%s] 目标站点返回了反爬验证页,无法解析搜索结果", p.Name())
    }
    return nil, fmt.Errorf("[%s] 解析搜索结果失败(页面结构可能已变更): %w", p.Name(), err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-check that the body looks like a results page before parsing
if !bytes.Contains(body, []byte("result")) && !bytes.Contains(body, []byte("搜索结果")) {
    return fmt.Errorf("response does not look like a results page")
}

Type guard

func looksLikeResultsPage(body []byte) bool {
    return bytes.Contains(body, []byte("<html")) &&
        !bytes.Contains(body, []byte("captcha")) &&
        !bytes.Contains(body, []byte("login"))
}

Try / catch

results, err := plugin.Search(ctx, keyword)
if err != nil && strings.Contains(err.Error(), "解析搜索结果失败") {
    // page shape changed or anti-bot page: alert/fallback, don't retry blindly
    log.Printf("site layout may have changed: %v", err)
    return fallbackSearch(keyword)
}

Prevention

When it happens

Trigger: parseListItems(body) returns an error because the fetched HTML lacks the expected list/result nodes: the site changed its DOM structure, returned a CAPTCHA/anti-bot interstitial, a login redirect page, or a 200-status error page instead of the search results markup.

Common situations: Target site redesign changes CSS selectors/element classes; anti-bot protection serving challenge pages with HTTP 200; region/CDN variants of the site with different markup; keyword triggering a 'no results' template the parser treats as malformed.

Related errors


AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07). Data as JSON: /api/errors/01053b8bd3a4dbbb. Report an issue: GitHub.

Appendix: source

Thrown at plugin/zlxapp/zlxapp.go:124

	setRequestHeaders(req, p.baseURL)

	resp, err := doRequestWithRetry(client, req)
	if err != nil {
		return nil, fmt.Errorf("[%s] 搜索请求失败: %w", p.Name(), err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseSize+1))
	if err != nil {
		return nil, fmt.Errorf("[%s] 读取搜索响应失败: %w", p.Name(), err)
	}
	if len(body) > maxResponseSize {
		return nil, fmt.Errorf("[%s] 搜索响应超过 %d 字节", p.Name(), maxResponseSize)
	}

	items, err := parseListItems(body)
	if err != nil {
		return nil, fmt.Errorf("[%s] 解析搜索结果失败: %w", p.Name(), err)
	}

	results := make([]model.SearchResult, 0, len(items))
	for _, item := range items {
		if result, ok := convertItem(item); ok {
			results = append(results, result)
		}
	}
	return plugin.FilterResultsByKeyword(deduplicateResults(results), keyword), nil
}

func setRequestHeaders(req *http.Request, refererBaseURL string) {
	req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36")
	req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8")
	req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
	req.Header.Set("Cache-Control", "no-cache")
	req.Header.Set("Referer", strings.TrimRight(refererBaseURL, "/")+"/")
}

View on GitHub (pinned to beaa561337)