fish2018/pansou · error

[ ] 搜索请求失败

Error message

[%s] 搜索请求失败: %w

What it means

searchImpl wraps any error returned by doRequestWithRetry — the HTTP GET to the duoduo search page failed after all configured retry attempts. This covers transport-level failures: DNS resolution, TCP connect, TLS handshake, context deadline exceeded, and proxy errors. The underlying net.Error / context error is preserved via %w.

Solutions

  1. Test connectivity to the site directly (curl -v the search URL) to confirm it is reachable.
  2. Increase DefaultTimeout in the plugin or the retry count in doRequestWithRetry if the site is slow.
  3. Check for required proxy settings; if the site needs one, configure the http.Client transport with a proxy.
  4. Handle the wrapped error with errors.As for *net.OpError / context.DeadlineExceeded and surface a clear message or fall back to another plugin.

Example fix

// before
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
    return nil, fmt.Errorf("[%s] 搜索请求失败: %w", p.Name(), err)
}
// after
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
    if errors.Is(err, context.DeadlineExceeded) {
        return nil, fmt.Errorf("[%s] 搜索超时,站点不可达或超时过短: %w", p.Name(), err)
    }
    return nil, fmt.Errorf("[%s] 搜索请求失败: %w", p.Name(), err)
}
Defensive patterns

Strategy: retry

Validate before calling

func canReach(urlStr string) bool {
    ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
    defer cancel()
    req, err := http.NewRequestWithContext(ctx, http.MethodHead, urlStr, nil)
    if err != nil { return false }
    resp, err := http.DefaultClient.Do(req)
    if err != nil { return false }
    resp.Body.Close()
    return true
}

Try / catch

if err != nil {
    var netErr net.Error
    switch {
    case errors.Is(err, context.DeadlineExceeded):
        // treat as timeout: lengthen timeout or skip source
    case errors.As(err, &netErr):
        // transient network issue: retry with backoff
    default:
        // unexpected: log and fall back to another plugin
    }
}

Prevention

When it happens

Trigger: doRequestWithRetry(req, client) exhausts its retry loop because every attempt returns a non-nil error (server unreachable, DNS failure, TLS errors, or the DefaultTimeout context expired).

Common situations: Target site tv.yydsys.top is down or blocked; developer is behind a firewall/proxy; DNS cannot resolve the domain; network is slow enough that DefaultTimeout expires on every attempt; running from a region where the site is geo-blocked.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at plugin/duoduo/duoduo.go:179

	// 3. 创建请求
	req, err := http.NewRequestWithContext(ctx, "GET", searchURL, nil)
	if err != nil {
		return nil, fmt.Errorf("[%s] 创建请求失败: %w", p.Name(), err)
	}
	
	// 4. 设置完整的请求头(避免反爬虫)
	req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
	req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8")
	req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
	req.Header.Set("Connection", "keep-alive")
	req.Header.Set("Upgrade-Insecure-Requests", "1")
	req.Header.Set("Cache-Control", "max-age=0")
	req.Header.Set("Referer", "https://tv.yydsys.top/")
	
	// 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)

View on GitHub (pinned to beaa561337)