fish2018/pansou · error
[ ] 解析搜索页面失败
Error message
[%s] 解析搜索页面失败: %w
What it means
Raised in Muou's searchAtBase when goquery.NewDocumentFromReader fails to parse the search response body as HTML. The body was empty, truncated, or not HTML — often an anti-bot challenge page or an encoding problem.
Solutions
- Read the body with io.ReadAll first and log a prefix on parse failure to see actual content
- Confirm resp.Body was not previously read (single consumption)
- Verify Content-Type of the response is text/html before parsing
- Let http.Client handle compression automatically (don't set raw Accept-Encoding unless decoding manually)
- Add a length check to skip/record empty bodies distinctly from parse errors
Example fix
// before
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
return nil, fmt.Errorf("[%s] 解析搜索页面失败: %w", p.Name(), err)
}
// after
body, rerr := io.ReadAll(resp.Body)
if rerr != nil {
return nil, fmt.Errorf("[%s] 读取搜索页面失败: %w", p.Name(), rerr)
}
if len(bytes.TrimSpace(body)) == 0 {
return nil, fmt.Errorf("[%s] 搜索页面响应为空", p.Name())
}
doc, err := goquery.NewDocumentFromReader(bytes.NewReader(body))
if err != nil {
return nil, fmt.Errorf("[%s] 解析搜索页面失败: %w", p.Name(), err)
} Defensive patterns
Strategy: validation
Validate before calling
body, err := io.ReadAll(resp.Body)
if err != nil { return err }
if len(bytes.TrimSpace(body)) == 0 {
return errEmptyBody
}
if !strings.Contains(resp.Header.Get("Content-Type"), "text/html") {
return fmt.Errorf("non-html response: %s", resp.Header.Get("Content-Type"))
}
doc, err := goquery.NewDocumentFromReader(bytes.NewReader(body)) Try / catch
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
log.Printf("search page parse failed: %v", err)
return nil, errParse // let caller try next mirror
} Prevention
- Validate Content-Type and body length before goquery parsing
- Read the body exactly once
- Log body prefixes to detect anti-bot challenge pages
- Handle compression via http.Client defaults
When it happens
Trigger: Triggered when the search page body cannot be parsed: empty response body, connection closed mid-download, gzip/deflate body not decoded, or a JS challenge page that is still invalid HTML.
Common situations: CDN serves a challenge/interstitial; response body already consumed elsewhere; server closes connection early under load; charset/encoding issues breaking the tokenizer.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/04db135c3e7e51e9.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/muou/muou.go:230
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)
}
// 6. 解析搜索结果页面
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
return nil, fmt.Errorf("[%s] 解析搜索页面失败: %w", p.Name(), err)
}
// 7. 提取搜索结果
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 *MuouAsyncPlugin) parseSearchItem(s *goquery.Selection, keyword string) model.SearchResult {
result := model.SearchResult{}View on GitHub (pinned to beaa561337)