fish2018/pansou · error
[ ] 解析搜索页面失败
Error message
[%s] 解析搜索页面失败: %w
What it means
searchAtBase wraps an error from goquery.NewDocumentFromReader: the labi mirror returned a 200 status but its body could not be parsed as HTML. The response was likely empty, truncated, compressed, or not HTML (e.g. a challenge or JSON error body).
Solutions
- Inspect the wrapped parse error and dump the first bytes of the body to see what was actually returned
- Do not set Accept-Encoding manually so net/http transparently decompresses before goquery reads it
- Treat this mirror as unhealthy and fall back to the next candidate base URL
- Retry once on short/truncated bodies before giving up on the mirror
Example fix
// before
req.Header.Set("Accept-Encoding", "gzip")
// after
// remove the header; let net/http handle decompression so goquery receives plain HTML Defensive patterns
Strategy: validation
Validate before calling
resp, err := client.Get(searchURL)
if err == nil {
defer resp.Body.Close()
if ct := resp.Header.Get("Content-Type"); !strings.Contains(ct, "text/html") {
// parse will fail; treat mirror as unhealthy
}
} Type guard
func looksLikeHTML(b []byte) bool {
s := strings.TrimSpace(strings.ToLower(string(b[:min(len(b), 512)])))
return strings.HasPrefix(s, "<!doctype html") || strings.HasPrefix(s, "<html")
} Try / catch
doc, err := searchAndParse(base, kw)
if err != nil && strings.Contains(err.Error(), "解析搜索页面失败") {
log.Printf("mirror %s returned non-HTML body: %v", base, err)
return tryNextMirror(kw)
} Prevention
- Don't set Accept-Encoding manually; ensure bodies arrive decompressed
- Verify Content-Type and body prefix before goquery parsing
- Quarantine mirrors that repeatedly serve empty/challenge 200 bodies
- Retest mirrors after proxy or CDN changes that can alter bodies
When it happens
Trigger: goquery.NewDocumentFromReader(resp.Body) errors after a 200 response: empty/short body from a broken mirror, manually-set Accept-Encoding causing raw gzip to reach the parser, or a non-HTML body.
Common situations: A half-dead mirror returning empty 200 pages, proxy/CDN mangling or truncating responses, custom transport without auto-decompression, anti-bot challenge served with a 200 status.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/7babe6734dd5ac0f.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/labi/labi.go:210
req.Header.Set("Upgrade-Insecure-Requests", "1")
req.Header.Set("Cache-Control", "max-age=0")
req.Header.Set("Referer", strings.TrimRight(baseURL, "/")+"/")
// 5. 发送请求(带重试机制)
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
return nil, fmt.Errorf("[%s] 搜索请求失败: %w", p.Name(), err)
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return nil, fmt.Errorf("[%s] 搜索请求返回状态码: %d", p.Name(), resp.StatusCode)
}
// 3. 解析搜索结果页面
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
return nil, fmt.Errorf("[%s] 解析搜索页面失败: %w", p.Name(), err)
}
// 4. 提取搜索结果
var results []model.SearchResult
doc.Find(".module-search-item").Each(func(i int, s *goquery.Selection) {
result := p.parseSearchItem(s, keyword)
if result.UniqueID != "" {
results = append(results, result)
}
})
return results, nil
}
// parseSearchItem 解析单个搜索结果项
func (p *LabiAsyncPlugin) parseSearchItem(s *goquery.Selection, keyword string) model.SearchResult {
result := model.SearchResult{}View on GitHub (pinned to beaa561337)