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
- Dump a sample of body on failure and compare the actual DOM against the selectors parseListItems expects; update the parser to the new structure.
- 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.
- Pin or update the plugin version to one matching the current site layout; check upstream for an existing fix.
- 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
- Snapshot real HTML fixtures and run parseListItems against them in CI to catch site-redesign breakage
- Detect CAPTCHA/login/anti-bot markers before treating parse failure as a code bug
- Treat empty results as empty, not malformed, when the site has a recognizable no-results template
- Log a short body excerpt on parse failure to speed up selector updates
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)