fish2018/pansou · error
[ ] 第 页搜索请求失败
Error message
[%s] 第%d页搜索请求失败: %w
What it means
searchPage wraps an error from the underlying HTTP client.Do for page N of the dy4k search. The preceding code classifies netErr.Timeout()/Temporary(), but regardless of type the request failed at the transport level after (possibly) retries. The wrapped cause is preserved for errors.Is/As inspection.
Solutions
- Read the debug output / unwrap the error to distinguish timeout vs reset vs DNS and act accordingly.
- Increase DefaultTimeout and add per-page throttling or a worker limit to avoid hammering the site.
- Configure a working proxy if the site is unreachable from the current network.
- Implement bounded retry with backoff for Temporary network errors before surfacing the failure.
Example fix
// before
return nil, 0, fmt.Errorf("[%s] 第%d页搜索请求失败: %w", p.Name(), page, err)
// after
var netErr net.Error
if errors.As(err, &netErr) && netErr.Timeout() {
return nil, 0, fmt.Errorf("[%s] 第%d页请求超时(考虑增大DefaultTimeout): %w", p.Name(), page, err)
}
return nil, 0, fmt.Errorf("[%s] 第%d页搜索请求失败: %w", p.Name(), page, err) Defensive patterns
Strategy: retry
Try / catch
var netErr net.Error
if errors.As(err, &netErr) {
if netErr.Timeout() {
// increase timeout or skip page
} else if ne, ok := err.(*net.OpError); ok && isTransient(ne) {
// retry with exponential backoff
}
} Prevention
- Bound concurrent page requests to avoid resets.
- Set DefaultTimeout generously for slow upstreams.
- Retry only transient network errors, with backoff.
- Check site/proxy reachability before batch scraping.
When it happens
Trigger: client.Do(req) returns an error on page N: context deadline (DefaultTimeout) exceeded, connection reset/refused, DNS failure, or proxy/SOCKS5 dial failure. The surrounding debug output explicitly checks net.Error Timeout/Temporary, so timeouts are a first-class trigger.
Common situations: Target site slow or blocking by IP so requests hang past DefaultTimeout; too many parallel page requests cause connection resets; no/broken proxy configured while the site requires one; IPv6 issues causing hangs.
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/dfeef7a34c83d91b.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/dy4k/dy4k.go:405
}
startTime := time.Now()
resp, err := p.doRequestWithRetry(req, client)
requestDuration := time.Since(startTime)
if err != nil {
debugPrintf("❌ [Dy4k DEBUG] HTTP请求失败 (耗时: %v): %v\n", requestDuration, err)
debugPrintf("❌ [Dy4k DEBUG] 错误类型分析:\n")
if netErr, ok := err.(*url.Error); ok {
fmt.Printf(" URL错误: %v\n", netErr.Err)
if netErr.Timeout() {
fmt.Printf(" -> 这是超时错误\n")
}
if netErr.Temporary() {
fmt.Printf(" -> 这是临时错误\n")
}
}
return nil, 0, fmt.Errorf("[%s] 第%d页搜索请求失败: %w", p.Name(), page, err)
}
defer resp.Body.Close()
debugPrintf("✅ [Dy4k DEBUG] HTTP请求成功 (耗时: %v)\n", requestDuration)
// 6. 检查状态码
debugPrintf("🔧 [Dy4k DEBUG] HTTP响应状态码: %d\n", resp.StatusCode)
if resp.StatusCode != 200 {
debugPrintf("❌ [Dy4k DEBUG] 状态码异常: %d\n", resp.StatusCode)
return nil, 0, fmt.Errorf("[%s] 第%d页请求返回状态码: %d", p.Name(), page, resp.StatusCode)
}
// 7. 读取并打印HTML响应
htmlBytes, err := io.ReadAll(resp.Body)
if err != nil {
return nil, 0, fmt.Errorf("[%s] 第%d页读取响应失败: %w", p.Name(), page, err)
}
View on GitHub (pinned to beaa561337)