fish2018/pansou · error

解析HTML失败

Error message

解析HTML失败: %w

What it means

The hdr4k plugin wraps any error from goquery.NewDocumentFromReader(resp.Body) — which reads the whole search-results HTML response — with this message. goquery only fails here if the underlying io.ReadAll of resp.Body fails, meaning the response body could not be read. It is a wrapper around a low-level network/IO failure that happened while draining the HTTP response.

Solutions

  1. Retry the search request; transient body-read failures usually succeed on a second attempt (the plugin already retries the request, but not the body read).
  2. Check network connectivity/proxy settings between the host and www.4khdr.cn.
  3. Inspect the wrapped %w error to identify the exact underlying IO failure.
  4. Verify the site is reachable in a browser and not blocking your IP/rate-limiting you.

Example fix

// before
resp, err := p.doRequestWithRetry(client, req, MaxRetries)
if err != nil { return nil, err }
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
    return nil, fmt.Errorf("解析HTML失败: %w", err)
}
// after
resp, err := p.doRequestWithRetry(client, req, MaxRetries)
if err != nil { return nil, err }
body, readErr := io.ReadAll(resp.Body)
resp.Body.Close()
if readErr != nil {
    return nil, fmt.Errorf("读取响应体失败(可重试): %w", readErr)
}
doc, err := goquery.NewDocumentFromReader(bytes.NewReader(body))
Defensive patterns

Strategy: retry

Try / catch

results, err := plugin.Search(keyword, ext)
if err != nil {
    var transient = strings.Contains(err.Error(), "解析HTML失败")
    if transient {
        time.Sleep(2 * time.Second)
        results, err = plugin.Search(keyword, ext) // one manual retry
    }
    if err != nil {
        log.Printf("hdr4k search failed: %v", err)
        return fallbackResults
    }
}

Prevention

When it happens

Trigger: Called in doSearch after a successful doRequestWithRetry; it triggers when reading resp.Body fails, e.g. the connection was reset or timed out mid-body, the server closed the connection early, or a proxy interrupted the transfer.

Common situations: Unstable connections to www.4khdr.cn, server-side connection drops on large search pages, VPN/proxy interference, or firewalls that cut long-lived response streams.

Related errors


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

Appendix: source

Thrown at plugin/hdr4k/hdr4k.go:161

		return nil, fmt.Errorf("创建请求失败: %w", err)
	}
	
	// 设置请求头
	req.Header.Set("User-Agent", getRandomUA())
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Set("Referer", "https://www.4khdr.cn/")
	
	// 发送请求(带重试)
	resp, err := p.doRequestWithRetry(client, req, MaxRetries)
	if err != nil {
		return nil, fmt.Errorf("请求失败: %w", err)
	}
	defer resp.Body.Close()
	
	// 解析HTML
	doc, err := goquery.NewDocumentFromReader(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("解析HTML失败: %w", err)
	}
	
	// 提取搜索结果
	var wg sync.WaitGroup
	resultChan := make(chan model.SearchResult, 20)
	errorChan := make(chan error, 20)
	
	// 创建信号量控制并发数
	semaphore := make(chan struct{}, MaxConcurrency)
	
	// 预先收集所有需要处理的项
	var items []*goquery.Selection
	
	// 将关键词转为小写,用于不区分大小写的比较
	lowerKeyword := strings.ToLower(keyword)
	
	// 将关键词按空格分割,用于支持多关键词搜索
	keywords := strings.Fields(lowerKeyword)

View on GitHub (pinned to beaa561337)