fish2018/pansou · error

[ ] 解析搜索页面失败

Error message

[%s] 解析搜索页面失败: %w

What it means

Raised in Muou's searchAtBase when goquery.NewDocumentFromReader fails to parse the search response body as HTML. The body was empty, truncated, or not HTML — often an anti-bot challenge page or an encoding problem.

Solutions

  1. Read the body with io.ReadAll first and log a prefix on parse failure to see actual content
  2. Confirm resp.Body was not previously read (single consumption)
  3. Verify Content-Type of the response is text/html before parsing
  4. Let http.Client handle compression automatically (don't set raw Accept-Encoding unless decoding manually)
  5. Add a length check to skip/record empty bodies distinctly from parse errors

Example fix

// before
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
    return nil, fmt.Errorf("[%s] 解析搜索页面失败: %w", p.Name(), err)
}
// after
body, rerr := io.ReadAll(resp.Body)
if rerr != nil {
    return nil, fmt.Errorf("[%s] 读取搜索页面失败: %w", p.Name(), rerr)
}
if len(bytes.TrimSpace(body)) == 0 {
    return nil, fmt.Errorf("[%s] 搜索页面响应为空", p.Name())
}
doc, err := goquery.NewDocumentFromReader(bytes.NewReader(body))
if err != nil {
    return nil, fmt.Errorf("[%s] 解析搜索页面失败: %w", p.Name(), err)
}
Defensive patterns

Strategy: validation

Validate before calling

body, err := io.ReadAll(resp.Body)
if err != nil { return err }
if len(bytes.TrimSpace(body)) == 0 {
    return errEmptyBody
}
if !strings.Contains(resp.Header.Get("Content-Type"), "text/html") {
    return fmt.Errorf("non-html response: %s", resp.Header.Get("Content-Type"))
}
doc, err := goquery.NewDocumentFromReader(bytes.NewReader(body))

Try / catch

doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
    log.Printf("search page parse failed: %v", err)
    return nil, errParse // let caller try next mirror
}

Prevention

When it happens

Trigger: Triggered when the search page body cannot be parsed: empty response body, connection closed mid-download, gzip/deflate body not decoded, or a JS challenge page that is still invalid HTML.

Common situations: CDN serves a challenge/interstitial; response body already consumed elsewhere; server closes connection early under load; charset/encoding issues breaking the tokenizer.

Related errors


AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07). Data as JSON: /api/errors/04db135c3e7e51e9. Report an issue: GitHub.

Appendix: source

Thrown at plugin/muou/muou.go:230

	req.Header.Set("Upgrade-Insecure-Requests", "1")
	req.Header.Set("Cache-Control", "max-age=0")
	req.Header.Set("Referer", strings.TrimRight(baseURL, "/")+"/")

	// 5. 发送请求(带重试机制)
	resp, err := p.doRequestWithRetry(req, client)
	if err != nil {
		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)
	}

	// 6. 解析搜索结果页面
	doc, err := goquery.NewDocumentFromReader(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("[%s] 解析搜索页面失败: %w", p.Name(), err)
	}

	// 7. 提取搜索结果
	var results []model.SearchResult

	doc.Find(".module-search-item").Each(func(i int, s *goquery.Selection) {
		result := p.parseSearchItem(s, keyword)
		if result.UniqueID != "" {
			results = append(results, result)
		}
	})

	return results, nil
}

// parseSearchItem 解析单个搜索结果项
func (p *MuouAsyncPlugin) parseSearchItem(s *goquery.Selection, keyword string) model.SearchResult {
	result := model.SearchResult{}

View on GitHub (pinned to beaa561337)