fish2018/pansou · error

[ ] 搜索请求返回状态码

Error message

[%s] 搜索请求返回状态码: %d

What it means

The djgou plugin returns this when the search HTTP response status code is not 200. The library treats any non-200 (403, 429, 5xx, redirects to challenge pages) as a failed search because it expects an HTML results page. The response body is closed and the code is reported.

Solutions

  1. Log the status code and compare against recent site behavior (403=blocked, 429=rate limited, 5xx=server-side)
  2. Slow down request rate / add jitter and respect backoff
  3. Verify headers (Referer, User-Agent, Cache-Control) still match what a browser sends
  4. If the site now redirects or challenges, update SiteURL/cookie handling in the plugin

Example fix

// before
if err != nil { return err } // generic handling
// after
var se *ErrSearchStatus
if errors.As(err, &se) && se.Code == 429 {
    time.Sleep(30 * time.Second)
    items, err = p.searchImpl(keyword)
}
Defensive patterns

Strategy: try-catch

Validate before calling

resp, err := client.Head(siteURL)
if err == nil && resp.StatusCode != 200 {
    log.Printf("site currently returns %d, expecting blocking", resp.StatusCode)
}
if resp != nil { resp.Body.Close() }

Try / catch

if err != nil && strings.Contains(err.Error(), "搜索返回状态码") {
    code := extractStatusCode(err) // parse from message
    switch code {
    case 429: time.Sleep(time.Minute); retry()
    case 403: rotateHeadersOrIP(); retry()
    default: giveUp()
    }
}

Prevention

When it happens

Trigger: searchImpl received a valid HTTP response but resp.StatusCode != 200 — e.g. anti-bot 403, rate-limit 429, 5xx from the origin, or an unexpected redirect target.

Common situations: Site behind WAF blocking datacenter IPs; request rate too high; missing/expired cookies or headers; site moved and now redirects to a page the client does not follow to a 200.

Related errors


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

Appendix: source

Thrown at plugin/djgou/djgou.go:145

	// 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 {
		if match := btwafURLRegex.FindStringSubmatch(string(body)); len(match) > 1 {
			challengeURL := match[1]
			if strings.HasPrefix(challengeURL, "/") {
				challengeURL = SiteURL + challengeURL
			}

View on GitHub (pinned to beaa561337)