fish2018/pansou · error
[ ] 解析详情页失败
Error message
[%s] 解析详情页失败: %w
What it means
goquery.NewDocumentFromReader parses the detail response body as HTML; this error wraps any parser failure. goquery's underlying golang.org/x/net/html parser is lenient, so this rarely triggers on valid text — it fires when the body is not HTML at all (binary, gzip-encoded-but-undeclared, or a truncated stream) or the body was already consumed.
Solutions
- Log the wrapped %w error and the first bytes of resp.Body to see what was actually returned
- Verify no custom Transport is interfering with gzip handling (let Go handle Content-Encoding automatically)
- Check Content-Type of the response; skip non-HTML responses before parsing
- Increase retry robustness: a transient truncated body usually succeeds on a later retry
Example fix
// before
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
return detailResult{}, fmt.Errorf("[%s] 解析详情页失败: %w", p.Name(), err)
}
// after
if ct := resp.Header.Get("Content-Type"); ct != "" && !strings.Contains(ct, "html") {
return detailResult{}, fmt.Errorf("[%s] 非HTML响应: %s", p.Name(), ct)
}
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
return detailResult{}, fmt.Errorf("[%s] 解析详情页失败: %w", p.Name(), err)
} Defensive patterns
Strategy: try-catch
Validate before calling
ct := resp.Header.Get("Content-Type")
if ct != "" && !strings.Contains(ct, "text/html") {
return skip // not an HTML page, parsing will fail
} Try / catch
if err != nil {
log.Printf("html parse failed: %v", err)
// fall back to returning an empty result for this detail page
return detailResult{}, nil
} Prevention
- Do not wrap the Transport's automatic gzip handling with manual decompression
- Validate Content-Type before parsing
- Retry once on parse failure — transient truncation is common
- Keep goquery and x/net/html updated for parser robustness fixes
When it happens
Trigger: Response body is not readable HTML: compressed content with wrong Content-Encoding, a binary error page, or an empty/corrupt stream that makes net/html parsing return an error.
Common situations: Server returns a challenge page or garbage bytes instead of the forum HTML; a proxy mangles content-encoding; double decompression when a custom Transport sets Accept-Encoding and also has DisableCompression=false.
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/5309951a8fd7a1f1.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/lou1/lou1.go:279
req, err := http.NewRequestWithContext(ctx, http.MethodGet, detailURL, nil)
if err != nil {
return detailResult{}, fmt.Errorf("[%s] 创建详情页请求失败: %w", p.Name(), err)
}
setHTMLHeaders(req, baseURL)
resp, err := p.doRequestWithRetry(req, client, maxRequestRetries)
if err != nil {
return detailResult{}, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return detailResult{}, fmt.Errorf("[%s] 详情页返回状态码: %d", p.Name(), resp.StatusCode)
}
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
return detailResult{}, fmt.Errorf("[%s] 解析详情页失败: %w", p.Name(), err)
}
content := doc.Find("div.message[isfirst='1']")
if content.Length() == 0 {
content = doc.Find(".message")
}
if content.Length() == 0 {
content = doc.Selection
}
content.Find("script, style").Remove()
links := extractLinksFromSelection(content)
links = filterQuarkLinks(links)
description := strings.TrimSpace(doc.Find("meta[name='description']").AttrOr("content", ""))
if description == "" {
description = truncateString(strings.TrimSpace(content.Text()), 200)
}View on GitHub (pinned to beaa561337)