fish2018/pansou · error

[ ] 搜索请求失败

Error message

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

What it means

The djgou plugin wraps any failure from its HTTP request retry loop (doRequestWithRetry) into a Chinese-language error meaning '[plugin] search request failed'. This library throws it when the site cannot be reached after all retries — DNS failure, timeout, TLS error, connection refused, or a request that never yields a response object. It preserves the underlying cause via %w.

Solutions

  1. Test basic reachability of the site: curl -I the SiteURL from the same host
  2. Check DNS/proxy environment (HTTP_PROXY/HTTPS_PROXY) and container egress rules
  3. Retry later or raise client/timeout settings if the site is intermittently slow
  4. Inspect the wrapped cause (%w) with errors.Unwrap/As(*url.Error) to identify the transport failure

Example fix

// before
items, err := p.searchImpl(keyword)
if err != nil { return err }
// after
items, err := p.searchImpl(keyword)
var urlErr *url.Error
if errors.As(err, &urlErr) && errors.Is(urlErr.Err, context.DeadlineExceeded) {
    return fmt.Errorf("site timed out, retry later: %w", err)
}
return err
Defensive patterns

Strategy: retry

Validate before calling

func siteReachable(siteURL string) error {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()
    req, _ := http.NewRequestWithContext(ctx, http.MethodGet, siteURL, nil)
    resp, err := http.DefaultClient.Do(req)
    if err != nil { return err }
    resp.Body.Close()
    return nil
}

Try / catch

items, err := p.searchImpl(keyword)
var urlErr *url.Error
if errors.As(err, &urlErr) {
    log.Printf("djgou transport error: %v (timeout=%v)", urlErr.Err, errors.Is(urlErr.Err, context.DeadlineExceeded))
    // schedule retry with backoff
}

Prevention

When it happens

Trigger: p.doRequestWithRetry(req, client) returns err != nil during searchImpl: all retry attempts fail at the transport level (connection refused, timeout, TLS handshake, context deadline).

Common situations: Target site (djgou) is down or blocking the host; no outbound network/DNS in the container; corporate proxy required but not configured; site IP changed; overly tight client timeout.

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/dba30820a87e08b2. Report an issue: GitHub.

Appendix: source

Thrown at plugin/djgou/djgou.go:140

	// 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/114.0.0.0 Safari/537.36")
	req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;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", SiteURL)

	// 5. 发送请求(带重试机制)
	resp, err := p.doRequestWithRetry(req, client)
	if err != nil {
		return nil, fmt.Errorf("[%s] 搜索请求失败: %w", p.Name(), err)
	}

	if resp.StatusCode != 200 {
		resp.Body.Close()
		return nil, fmt.Errorf("[%s] 搜索请求返回状态码: %d", p.Name(), resp.StatusCode)
	}

	// 6. 读取并解析搜索结果页面。部分节点先返回 BTWAF JS 跳转页。
	body, err := io.ReadAll(resp.Body)
	resp.Body.Close()
	if err != nil {
		return nil, fmt.Errorf("[%s] 读取搜索页面失败: %w", p.Name(), err)
	}
	doc, err := goquery.NewDocumentFromReader(strings.NewReader(string(body)))
	if err != nil {
		return nil, fmt.Errorf("[%s] 解析搜索页面失败: %w", p.Name(), err)
	}
	if doc.Find("article.post-item-row").Length() == 0 {

View on GitHub (pinned to beaa561337)