fish2018/pansou · error
[ ] HTML解析失败
Error message
[%s] HTML解析失败: %w
What it means
jutoushe searchImpl wraps errors from goquery.NewDocumentFromReader, which parses the response body as HTML. This fails when the body is not valid HTML — empty body, truncated response, JSON error page, or compressed content not decompressed.
Solutions
- Read the body first and check it is non-empty before parsing
- Verify Content-Encoding handling (set Accept-Encoding or use an auto-decompressing transport)
- Log the first bytes of the body to see what the server actually returned
- Check that resp.Body was not already consumed by a prior read
Example fix
// before
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil { return nil, fmt.Errorf("[%s] HTML解析失败: %w", p.Name(), err) }
// after
body, rerr := io.ReadAll(resp.Body)
if rerr != nil || len(bytes.TrimSpace(body)) == 0 {
return nil, fmt.Errorf("[%s] empty or unreadable response body", p.Name())
}
doc, err := goquery.NewDocumentFromReader(bytes.NewReader(body)) Defensive patterns
Strategy: fallback
Validate before calling
body, _ := io.ReadAll(io.LimitReader(resp.Body, 256))
if len(bytes.TrimSpace(body)) == 0 {
return errors.New("empty response body; skip parsing")
} Try / catch
results, err := plugin.Search(keyword)
if err != nil {
if strings.Contains(err.Error(), "HTML解析失败") {
return useAlternativeSource(keyword) // fall back to another plugin
}
return err
} Prevention
- Check body non-empty before HTML parsing
- Ensure gzip/charset handling on the transport
- Log first body bytes when parsing fails
- Fall back to another search source when parsing fails
When it happens
Trigger: goquery.NewDocumentFromReader(resp.Body) errors: empty response body, charset/gzip issues, or a non-HTML response (e.g. a JSON error from a CDN).
Common situations: Anti-bot layer returns an empty or challenge page; response gzip-compressed but not handled; connection closed early producing truncated HTML.
Understand the failure class
Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/dede525398695bd6.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/jutoushe/jutoushe.go:82
req.Header.Set("Connection", "keep-alive")
req.Header.Set("Referer", baseURL+"/")
// 5. 发送HTTP请求(带重试机制)
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
return nil, fmt.Errorf("[%s] 搜索请求失败: %w", p.Name(), err)
}
defer resp.Body.Close()
// 6. 检查状态码
if resp.StatusCode != 200 {
return nil, fmt.Errorf("[%s] 请求返回状态码: %d", p.Name(), resp.StatusCode)
}
// 7. 解析搜索结果页面
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
return nil, fmt.Errorf("[%s] HTML解析失败: %w", p.Name(), err)
}
// 8. 提取搜索结果
var results []model.SearchResult
doc.Find("ul.erx-list li.item").Each(func(i int, s *goquery.Selection) {
// 提取标题和链接
linkElem := s.Find(".a a.main")
title := strings.TrimSpace(linkElem.Text())
detailPath, exists := linkElem.Attr("href")
if !exists || title == "" {
return // 跳过无效项
}
// 构建完整的详情页URL
detailURL := baseURL + detailPath
// 提取发布时间View on GitHub (pinned to beaa561337)