fish2018/pansou · error
[ ] HTML解析失败
Error message
[%s] HTML解析失败: %w
What it means
ClmaoPlugin.searchPage fails while parsing the decoded HTML with goquery.NewDocumentFromReader. goquery only errors when the underlying reader fails (its HTML parser is error-tolerant), so this usually indicates the decoded payload reader failed or an empty stream was handed in. Note: most real parse problems (no results found) do NOT produce this error — they surface as empty results from extractSearchResults.
Solutions
- Log the first N bytes of decodedHTML to confirm it is actually search-results HTML and not a CAPTCHA/error page.
- Check decodeModernPayload for edge cases that could corrupt or empty the payload (e.g. wrong base64/encoding assumptions after a site update).
- Goquery itself rarely errors — verify the error comes from the reader, and treat empty decodedHTML as an empty-result case instead.
- If the site changed markup so modern/legacy parsing both fail, update the selectors in parseModernSearchResults/extractSearchResults.
Example fix
// before
doc, err := goquery.NewDocumentFromReader(strings.NewReader(decodedHTML))
if err != nil {
return nil, fmt.Errorf("[%s] HTML解析失败: %w", p.Name(), err)
}
// after
if strings.TrimSpace(decodedHTML) == "" {
return nil, fmt.Errorf("[%s] 空响应,可能被反爬拦截", p.Name())
}
doc, err := goquery.NewDocumentFromReader(strings.NewReader(decodedHTML))
if err != nil {
return nil, fmt.Errorf("[%s] HTML解析失败: %w", p.Name(), err)
} Defensive patterns
Strategy: fallback
Validate before calling
func looksLikeSearchResults(html string) bool {
return strings.Contains(html, "result") || strings.Contains(html, "search")
} Try / catch
results, err := p.searchPage(client, keyword, page)
if err != nil {
if strings.Contains(err.Error(), "HTML解析失败") {
// decoded payload was not parseable HTML — likely CAPTCHA or changed page
log.Printf("clmao returned non-HTML payload; possible anti-bot page")
return nil, err
}
return nil, err
} Prevention
- Verify decodeModernPayload output before parsing (non-empty, starts with <html or <!doctype).
- Treat CAPTCHA/challenge pages as a distinct case, not a parse error.
- Update selectors when the site redesigns; test parsing against a saved HTML fixture.
- Log a body snippet on failure to speed diagnosis.
When it happens
Trigger: After decodeModernPayload produced decodedHTML, strings.NewReader(decodedHTML) is passed to goquery.NewDocumentFromReader and returns a non-nil err (reader/IO failure), so searchPage returns "[clmao] HTML解析失败: %w".
Common situations: decodeModernPayload returned corrupted/empty output on an unexpected page (e.g. a CAPTCHA or error page fed through the decoder); a bug in the decode step produced an invalid reader state; extremely large decoded payloads hitting memory limits.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/74664f790df90180.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/clmao/clmao.go:213
}
// 读取响应体内容
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("[%s] 读取响应失败: %w", p.Name(), err)
}
decodedHTML := decodeModernPayload(string(body))
if decodedHTML != string(body) {
if modernResults := p.parseModernSearchResults(client, decodedHTML); len(modernResults) > 0 {
return modernResults, nil
}
}
// 兼容旧模板
doc, err := goquery.NewDocumentFromReader(strings.NewReader(decodedHTML))
if err != nil {
return nil, fmt.Errorf("[%s] HTML解析失败: %w", p.Name(), err)
}
// 提取搜索结果
return p.extractSearchResults(doc), nil
}
func decodeModernPayload(raw string) string {
match := modernPayloadRegex.FindStringSubmatch(raw)
if len(match) < 2 {
return raw
}
decoded, err := base64.StdEncoding.DecodeString(match[1])
if err != nil {
return raw
}
text, err := url.PathUnescape(string(decoded))
if err != nil {
return string(decoded)View on GitHub (pinned to beaa561337)