fish2018/pansou · error
[ ] HTML解析失败
Error message
[%s] HTML解析失败: %w
What it means
searchPage wraps the error from goquery.NewDocumentFromReader, which parses the response body as HTML using the goquery/html parser. goquery's HTML parser is extremely lenient, so this error almost always means the body was not parseable at all — e.g. empty body, binary/compressed content, or garbage — rather than merely malformed markup.
Solutions
- Log the first bytes / length of body to see what was actually received
- Check the Content-Type and Content-Encoding response headers and add gzip handling if needed
- Send realistic Accept/Accept-Encoding headers via setRequestHeaders to get proper HTML
- Verify the site still serves the expected search HTML (open searchURL in a browser)
- If the site's markup changed, update extractSearchResults selectors; the parse error itself usually indicates non-HTML content
Defensive patterns
Strategy: fallback
Validate before calling
// check response looks like HTML before parsing
cType := resp.Header.Get("Content-Type")
if !strings.Contains(cType, "text/html") {
return fmt.Errorf("unexpected content-type: %s", cType)
}
if len(body) == 0 {
return fmt.Errorf("empty response body")
} Type guard
func looksLikeHTML(b []byte) bool {
s := strings.TrimSpace(string(b))
return len(s) > 0 && (strings.HasPrefix(s, "<") || strings.Contains(s[:min(200, len(s))], "<html"))
} Try / catch
results, err := p.Search(keyword, ext)
if err != nil {
if strings.Contains(err.Error(), "HTML解析失败") {
// fall back to another search plugin; log body sample for diagnosis
}
} Prevention
- Send explicit Accept: text/html and correct Accept-Encoding headers
- Check Content-Type/Content-Encoding before assuming HTML
- Log a prefix of the body when parsing fails to detect WAF pages or compression issues
- Keep goquery updated; verify site markup after upstream changes
When it happens
Trigger: goquery.NewDocumentFromReader(strings.NewReader(string(body))) fails: body is nil/garbled, served with an unexpected content encoding that wasn't decompressed, or the site returned binary data with a 200 status.
Common situations: Site changed response format or now serves compressed content without proper Accept-Encoding handling; response body empty because of a silent WAF block; CDN serving an error asset.
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/cf32fd340112b15d.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/wuji/wuji.go:209
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)
}
// 读取响应体内容
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("[%s] 读取响应失败: %w", p.Name(), err)
}
// 解析HTML
doc, err := goquery.NewDocumentFromReader(strings.NewReader(string(body)))
if err != nil {
return nil, fmt.Errorf("[%s] HTML解析失败: %w", p.Name(), err)
}
// 提取搜索结果
return p.extractSearchResults(doc), nil
}
// extractSearchResults 提取搜索结果
func (p *WujiPlugin) extractSearchResults(doc *goquery.Document) []model.SearchResult {
var results []model.SearchResult
// 查找所有搜索结果
doc.Find("table.file-list tbody tr").Each(func(i int, s *goquery.Selection) {
result := p.parseSearchResult(s)
if result.Title != "" {
results = append(results, result)
}
})
View on GitHub (pinned to beaa561337)