fish2018/pansou · error
[ ] 解析搜索结果失败
Error message
[%s] 解析搜索结果失败: %w
What it means
fetchSearch parses the response with goquery.NewDocumentFromReader over an io.LimitReader(resp.Body, 6<<20) and wraps failures with this message. Parsing fails when the body (capped at 6 MiB) is not valid HTML — JSON error, captcha page in another format, or corrupt/truncated stream.
Solutions
- Check Content-Type and log a body snippet to confirm what was actually returned
- Raise or remove the 6 MiB LimitReader cap if truncation is the cause
- Retry once on parse failure — transient network truncation often resolves
- Validate encoding (GBK vs UTF-8 common on Discuz forums) and convert before parsing
- If the site changed response format, update parsing or disable the mirror
Example fix
// before
doc, err := goquery.NewDocumentFromReader(io.LimitReader(resp.Body, 6<<20))
if err != nil {
return nil, fmt.Errorf("[%s] 解析搜索结果失败: %w", p.Name(), err)
}
// after
raw, err := io.ReadAll(io.LimitReader(resp.Body, 12<<20))
if err != nil {
return nil, fmt.Errorf("[%s] 读取响应失败: %w", p.Name(), err)
}
doc, err := goquery.NewDocumentFromReader(bytes.NewReader(raw))
if err != nil {
return nil, fmt.Errorf("[%s] 解析搜索结果失败 (len=%d): %w", p.Name(), len(raw), err)
} Defensive patterns
Strategy: validation
Validate before calling
ct := resp.Header.Get("Content-Type")
if !strings.Contains(ct, "text/html") {
return fmt.Errorf("leso returned non-HTML: %q", ct)
} Try / catch
doc, err := plugin.fetchSearch(client, baseURL, keyword)
if err != nil {
if strings.Contains(err.Error(), "解析搜索结果失败") {
// one clean retry often fixes transient truncation
if doc, retryErr := plugin.fetchSearch(client, baseURL, keyword); retryErr == nil {
return doc, nil
}
}
return err
} Prevention
- Verify Content-Type before HTML parsing
- Convert GBK/GB2312 responses to UTF-8 before parsing
- Size the LimitReader cap above the largest expected page
- Retry once on parse failure to absorb transient truncation
- Disable mirrors that consistently return unparseable bodies
When it happens
Trigger: goquery.NewDocumentFromReader returns an error after reading at most 6 MiB of the response body: malformed HTML, non-HTML content type, or body cut off mid-stream (by the limit reader or proxy).
Common situations: Huge response truncated at 6 MiB producing unbalanced markup goquery can't finalize; anti-bot JSON challenge; misrouted response from a transparent proxy; encoding issues producing invalid bytes.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/de3e4895caa25857.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/leso/leso.go:157
ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
defer cancel()
req, err := http.NewRequestWithContext(ctx, http.MethodPost, baseURL+"/search.php?searchsubmit=yes", strings.NewReader(form.Encode()))
if err != nil {
return nil, fmt.Errorf("[%s] 创建搜索请求失败: %w", p.Name(), err)
}
setHeaders(req, baseURL+"/")
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("[%s] 搜索请求失败: %w", p.Name(), err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("[%s] 搜索请求返回 HTTP %d", p.Name(), resp.StatusCode)
}
doc, err := goquery.NewDocumentFromReader(io.LimitReader(resp.Body, 6<<20))
if err != nil {
return nil, fmt.Errorf("[%s] 解析搜索结果失败: %w", p.Name(), err)
}
return doc, nil
}
func (p *Plugin) fetchDetail(client *http.Client, item searchItem) (model.SearchResult, bool) {
ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
defer cancel()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, item.detailURL, nil)
if err != nil {
return model.SearchResult{}, false
}
setHeaders(req, baseURL+"/")
resp, err := client.Do(req)
if err != nil {
return model.SearchResult{}, false
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {View on GitHub (pinned to beaa561337)