fish2018/pansou · error
[ ] 第 页搜索请求失败
Error message
[%s] 第%d页搜索请求失败: %w
What it means
This error wraps a failed per-page HTTP search request in the yunsou plugin's fetchPage. doRequestWithRetry exhausted all retry attempts (network error or non-200 status) and the underlying failure is chained with %w so the root cause (e.g. the '重试 %d 次后仍然失败' error) is preserved. It indicates the plugin could not retrieve the HTML for that search page at all.
Solutions
- Inspect the wrapped cause (%w chain) to see if it is a timeout, connection error, or bad status code.
- Verify network connectivity and that wpys.cc is reachable (curl -I https://wpys.cc/).
- Update/rotate User-Agent, Referer, and other headers if the site added anti-bot protection.
- Increase maxRetries or retry backoff in doRequestWithRetry for flaky networks.
- Reduce requested pages so one bad page does not fail the whole search.
Example fix
// before
return nil, fmt.Errorf("[%s] 第%d页搜索请求失败: %w", p.Name(), page, err)
// after
var resp *http.Response
resp, err = p.doRequestWithRetry(req, client)
if err != nil {
log.Printf("[%s] 第%d页搜索请求失败(跳过): %v", p.Name(), page, err)
return nil, nil // degrade gracefully instead of aborting search
} Defensive patterns
Strategy: retry
Validate before calling
func checkReachable(url string) error {
resp, err := http.Head(url)
if err != nil { return err }
defer resp.Body.Close()
if resp.StatusCode != 200 { return fmt.Errorf("站点状态码 %d", resp.StatusCode) }
return nil
} Try / catch
doc, err := p.fetchPage(client, keyword, page)
if err != nil {
log.Printf("跳过第%d页: %v", page, err)
return resultsSoFar, nil // partial results instead of abort
} Prevention
- Pre-flight check site reachability with a HEAD request
- Keep User-Agent/Referer headers current
- Add exponential backoff between retries
- Cap concurrent requests to avoid rate limiting
When it happens
Trigger: doRequestWithRetry returns an error after maxRetries failures in fetchPage — all attempts timed out, connection was refused/reset, or every retry returned a non-200 status code.
Common situations: Site wpys.cc is down or blocked, the User-Agent/Referer anti-scrape headers are no longer sufficient and the site returns 403/503, DNS failure, or the machine has no internet/proxy access.
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/07d20460d9614a36.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/yunsou/yunsou.go:129
func (p *YunsouAsyncPlugin) fetchPage(ctx context.Context, client *http.Client, keyword string, page int) (*goquery.Document, error) {
pathKeyword := url.PathEscape(keyword)
path := pathKeyword
if page > 1 {
path = fmt.Sprintf("%s-%d", pathKeyword, page)
}
requestURL := fmt.Sprintf(searchURLTemplate, path)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, requestURL, nil)
if err != nil {
return nil, fmt.Errorf("[%s] 创建第%d页请求失败: %w", p.Name(), page, err)
}
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36")
req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8")
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
req.Header.Set("Referer", "https://wpys.cc/")
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
return nil, fmt.Errorf("[%s] 第%d页搜索请求失败: %w", p.Name(), page, err)
}
defer resp.Body.Close()
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
return nil, fmt.Errorf("[%s] 解析第%d页失败: %w", p.Name(), page, err)
}
return doc, nil
}
func (p *YunsouAsyncPlugin) doRequestWithRetry(req *http.Request, client *http.Client) (*http.Response, error) {
var lastErr error
for attempt := 0; attempt < maxRetries; attempt++ {
if attempt > 0 {
time.Sleep(time.Duration(1<<(attempt-1)) * 200 * time.Millisecond)
}
resp, err := client.Do(req.Clone(req.Context()))
if err == nil && resp.StatusCode == http.StatusOK {
return resp, nilView on GitHub (pinned to beaa561337)