fish2018/pansou · error

[ ] HTML解析失败

Error message

[%s] HTML解析失败: %w

What it means

goquery.NewDocumentFromReader failed parsing the search response body. goquery returns errors from reading the body stream, not from HTML syntax, so this indicates the response body could not be read (truncated connection, decompression failure, or consumed stream).

Solutions

  1. Retry the request; truncation is often transient.
  2. Remove manual Accept-Encoding headers or enable automatic decompression on the http.Transport.
  3. Buffer the body with io.ReadAll first so it can be logged on failure and parsed from memory.
  4. Check for middleware/debug code reading resp.Body earlier and replace with io.TeeReader.

Example fix

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

Strategy: retry

Validate before calling

body, err := io.ReadAll(resp.Body)
if err != nil || len(bytes.TrimSpace(body)) == 0 {
    return // empty/truncated body, retry
}

Type guard

func isBodyReadError(err error) bool {
    return err != nil && (errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, io.EOF))
}

Try / catch

results, err := plugin.Search(keyword)
if err != nil && strings.Contains(err.Error(), "HTML解析失败") {
    select {
    case <-time.After(5 * time.Second):
        return plugin.Search(keyword) // one retry for transient body errors
    }
}

Prevention

When it happens

Trigger: Reading resp.Body fails after a 200 response: connection reset mid-body, gzip/br content not decoded (Accept-Encoding set manually), or body partially consumed before parsing.

Common situations: A proxy or anti-bot layer returned an encoded/compressed body the client didn't decode; flaky network truncating the page; debug logging code accidentally reading resp.Body before goquery.

Related errors


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

Appendix: source

Thrown at plugin/qingying/qingying.go:143

	}
	
	p.setHeaders(req, baseURL)
	
	resp, err := p.doRequestWithRetry(req, client)
	if err != nil {
		return nil, fmt.Errorf("[%s] 搜索请求失败: %w", p.Name(), err)
	}
	defer resp.Body.Close()
	
	debugPrintf("📡 HTTP状态码: %d\n", resp.StatusCode)
	
	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("[%s] 请求返回状态码: %d", p.Name(), resp.StatusCode)
	}
	
	doc, err := goquery.NewDocumentFromReader(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("[%s] HTML解析失败: %w", p.Name(), err)
	}
	
	var items []searchItem
	doc.Find("div.module-search-item").Each(func(i int, s *goquery.Selection) {
		link := s.Find(".video-info .video-info-header h3 a")
		href, exists := link.Attr("href")
		if !exists {
			debugPrintf("⚠️ 第%d个结果没有href属性\n", i+1)
			return
		}
		
		title := strings.TrimSpace(link.Text())
		if title == "" {
			title, _ = link.Attr("title")
			title = strings.TrimSpace(title)
		}
		
		if title == "" {

View on GitHub (pinned to beaa561337)