fish2018/pansou · error

详情页HTML解析失败

Error message

详情页HTML解析失败: %w

What it means

The downloaded detail-page HTML is parsed with goquery.NewDocumentFromReader. goquery's HTML parser (go.net/html) almost never errors on real input, so this error indicates malformed input such as a nil reader, or an empty/invalid body stream failing to parse. When it fires, the wrapped error comes from the underlying html parser.

Solutions

  1. Verify body is fully read before parsing (io.ReadAll succeeded) and non-empty; skip empty bodies earlier.
  2. If streaming resp.Body into goquery directly, close/replace that path — read fully first so parse errors are separated from network errors.
  3. Check the wrapped parse error via %w unwrap; go.net/html parse errors usually mean the input reader failed, not the HTML content.
  4. Add a guard: if len(body) == 0 return a clearer 'empty detail page' error before parsing.

Example fix

// before
doc, err := goquery.NewDocumentFromReader(strings.NewReader(string(body)))
if err != nil {
    return "", fmt.Errorf("详情页HTML解析失败: %w", err)
}
// after
if len(bytes.TrimSpace(body)) == 0 {
    return "", fmt.Errorf("详情页响应为空: %s", detailURL)
}
doc, err := goquery.NewDocumentFromReader(strings.NewReader(string(body)))
if err != nil {
    return "", fmt.Errorf("详情页HTML解析失败: %w", err)
}
Defensive patterns

Strategy: validation

Validate before calling

if len(bytes.TrimSpace(body)) == 0 {
    return fmt.Errorf("empty detail page body")
}
// safe to parse
doc, err := goquery.NewDocumentFromReader(strings.NewReader(string(body)))

Try / catch

doc, err := goquery.NewDocumentFromReader(strings.NewReader(string(body)))
if err != nil {
    return fmt.Errorf("parse failed: %w", err)
}

Prevention

When it happens

Trigger: goquery.NewDocumentFromReader(strings.NewReader(string(body))) returns an error — practically only when body is produced by a failing/nil reader or the parse hits a fatal reader error. With an in-memory strings.Reader over already-read bytes this is an edge case.

Common situations: A code path change passing a nil or already-closed body reader; extremely truncated responses reused downstream; custom modifications to the function that stream the body directly into goquery while the network stream errors.

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/7881309a39040dc6. Report an issue: GitHub.

Appendix: source

Thrown at plugin/wuji/wuji.go:335

		return "", fmt.Errorf("详情页请求失败: %w", err)
	}
	defer resp.Body.Close()
	
	// 检查状态码
	if resp.StatusCode != 200 {
		return "", fmt.Errorf("详情页返回状态码: %d", resp.StatusCode)
	}
	
	// 读取响应体内容
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", fmt.Errorf("读取详情页响应失败: %w", err)
	}
	
	// 解析HTML
	doc, err := goquery.NewDocumentFromReader(strings.NewReader(string(body)))
	if err != nil {
		return "", fmt.Errorf("详情页HTML解析失败: %w", err)
	}
	
	// 提取磁力链接
	magnetInput := doc.Find("input#input-magnet")
	if magnetInput.Length() == 0 {
		return "", fmt.Errorf("未找到磁力链接输入框")
	}
	
	magnetLink, exists := magnetInput.Attr("value")
	if !exists || magnetLink == "" {
		return "", fmt.Errorf("磁力链接为空")
	}
	
	// 存入缓存
	magnetCache.Store(detailURL, magnetCacheEntry{
		MagnetLink: magnetLink,
		Timestamp:  time.Now(),
	})

View on GitHub (pinned to beaa561337)