fish2018/pansou · error

[ ] API 失败且网页搜索返回状态码 (API: )

Error message

[%s] API 失败且网页搜索返回状态码 %d (API: %v)

What it means

In searchWeb, after the fallback HTML page loads, a non-200 status is reported with this message, which also embeds the original API failure (apiErr) since the web path only runs after the API path failed. It means the site answered but refused the scraping request.

Solutions

  1. Log the status code and check for HTML challenge pages in the body
  2. Update User-Agent/Accept/Referer headers to match a current browser and the live domain
  3. Add rate limiting or backoff between scraping requests
  4. Update SearchWebURL when the site rotates domains

Example fix

// before
if resp.StatusCode != http.StatusOK {
    return nil, fmt.Errorf("[%s] API 失败且网页搜索返回状态码 %d (API: %v)", p.Name(), resp.StatusCode, apiErr)
}
// after
if resp.StatusCode != http.StatusOK {
    return nil, fmt.Errorf("[%s] API 失败且网页搜索返回状态码 %d (API: %v)", p.Name(), resp.StatusCode, apiErr)
}
// plus: refresh headers per live site, e.g.
req.Header.Set("Referer", "https://feikuai.in/") // verify current domain
Defensive patterns

Strategy: retry

Validate before calling

// slow the scrape rate to stay under throttling thresholds
rateLimiter := rate.NewLimiter(rate.Every(500*time.Millisecond), 1)
_ = rateLimiter.Wait(ctx) // before each search call

Try / catch

results, err := plugin.Search(keyword)
if err != nil && strings.Contains(err.Error(), "网页搜索返回状态码") {
    if strings.Contains(err.Error(), "429") {
        time.Sleep(10 * time.Second)
        results, err = plugin.Search(keyword) // backoff retry
    } else if strings.Contains(err.Error(), "403") {
        log.Printf("feikuai WAF block; refresh headers or rotate proxy")
    }
}

Prevention

When it happens

Trigger: resp.StatusCode != http.StatusOK in searchWeb, typically 403 (anti-bot/WAF), 429 (rate limit), or 404/301 after a domain change, while the API path had already errored.

Common situations: Cloudflare or WAF challenge page for scraping requests, outdated Referer (https://feikuai.in/) after domain rotation, too-frequent scraping triggering throttling.

Related errors


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

Appendix: source

Thrown at plugin/feikuai/feikuai.go:189

func (p *FeikuaiPlugin) searchWeb(client *http.Client, keyword string, apiErr error) ([]model.SearchResult, error) {
	ctx, cancel := context.WithTimeout(context.Background(), DefaultTimeout)
	defer cancel()
	searchURL := SearchWebURL + "?wd=" + url.QueryEscape(keyword) + "&ext=1"
	req, err := http.NewRequestWithContext(ctx, http.MethodGet, searchURL, nil)
	if err != nil {
		return nil, fmt.Errorf("[%s] 创建网页搜索请求失败: %w", p.Name(), err)
	}
	req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/136.0.0.0 Safari/537.36")
	req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
	req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
	req.Header.Set("Referer", "https://feikuai.in/")
	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("[%s] API 失败且网页搜索请求失败: %v (API: %v)", p.Name(), err, apiErr)
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("[%s] API 失败且网页搜索返回状态码 %d (API: %v)", p.Name(), resp.StatusCode, apiErr)
	}
	doc, err := goquery.NewDocumentFromReader(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("[%s] 网页搜索 HTML 解析失败: %w", p.Name(), err)
	}

	results := make([]model.SearchResult, 0, 64)
	seen := make(map[string]struct{})
	add := func(linkURL, title, content string, datetime time.Time) {
		linkURL = strings.TrimSpace(linkURL)
		if linkURL == "" {
			return
		}
		linkType := util.GetLinkType(linkURL)
		if linkType == "" || linkType == "others" {
			return
		}
		if _, ok := seen[linkURL]; ok {

View on GitHub (pinned to beaa561337)