fish2018/pansou · error

[ ] HTML解析失败

Error message

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

What it means

goquery.NewDocumentFromReader failed to parse the kkv search response body as HTML. This happens when the 200 response is not valid parseable HTML — e.g. a compressed/binary body, truncated response, or a challenge page that breaks the parser.

Solutions

  1. Check the wrapped parse error; if it mentions gzip/zlib, let net/http auto-decompress by not setting Accept-Encoding manually
  2. Dump the first bytes of resp.Body to confirm the 200 response is actually HTML and not empty or a challenge page
  3. Check the proxy/CDN in front of the scraper isn't corrupting or truncating bodies
  4. Retry the request if the body was truncated by a flaky connection (doRequestWithRetry already retries transport errors, but not short bodies)

Example fix

// before
req.Header.Set("Accept-Encoding", "gzip, deflate")
// after
// omit Accept-Encoding so net/http transparently decompresses for goquery
Defensive patterns

Strategy: validation

Validate before calling

head, _ := client.Head(searchURL)
ct := head.Header.Get("Content-Type")
if !strings.Contains(ct, "text/html") {
    // expect HTML parse failure; skip source
}

Type guard

func looksLikeHTML(b []byte) bool {
    s := strings.TrimSpace(strings.ToLower(string(b[:min(len(b), 512)])))
    return strings.HasPrefix(s, "<!doctype html") || strings.HasPrefix(s, "<html")
}

Try / catch

if err != nil {
    if strings.Contains(err.Error(), "HTML解析失败") {
        log.Printf("non-HTML 200 body from source, skipping: %v", err)
        return nil
    }
    return err
}

Prevention

When it happens

Trigger: goquery.NewDocumentFromReader(resp.Body) returns err after a 200 status: body is gzip-encoded but not auto-decompressed, connection truncated mid-body, or the body is JSON/empty instead of HTML.

Common situations: A custom http.Client without automatic gzip handling receiving encoded bytes, a proxy returning an error page or empty 200 body, the site serving a Cloudflare challenge that goquery chokes on.

Related errors


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

Appendix: source

Thrown at plugin/kkv/kkv.go:142

	}
	
	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("article.post").Each(func(i int, s *goquery.Selection) {
		link := s.Find(".entry-header h2.entry-title a")
		href, exists := link.Attr("href")
		if !exists {
			debugPrintf("⚠️ 第%d个结果没有href属性\n", i+1)
			return
		}
		
		title := strings.TrimSpace(link.Text())
		if title == "" {
			debugPrintf("⚠️ 第%d个结果标题为空\n", i+1)
			return
		}
		
		re := regexp.MustCompile(`\?p=(\d+)`)

View on GitHub (pinned to beaa561337)