{"record":{"id":"78f4d89082001a13","repo":"fish2018/pansou","slug":"s-d-78f4d8","errorCode":null,"errorMessage":"[%s] 搜索返回状态码: %d","messagePattern":"\\[(.+?)\\] 搜索返回状态码: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/daishudj/daishudj.go","lineNumber":142,"sourceCode":"\n\tsearchURL := fmt.Sprintf(\"https://www.daishuduanju.com/?s=%s\", url.QueryEscape(keyword))\n\tctx, cancel := context.WithTimeout(context.Background(), searchTimeout)\n\tdefer cancel()\n\n\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, searchURL, nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 创建搜索请求失败: %w\", p.Name(), err)\n\t}\n\tsetCommonHeaders(req, \"https://www.daishuduanju.com/\")\n\n\tresp, err := p.doRequestWithRetry(req, client)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 搜索请求失败: %w\", p.Name(), err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"[%s] 搜索返回状态码: %d\", p.Name(), resp.StatusCode)\n\t}\n\n\tdoc, err := goquery.NewDocumentFromReader(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 解析搜索页面失败: %w\", p.Name(), err)\n\t}\n\n\tvar (\n\t\tresults []model.SearchResult\n\t\twg      sync.WaitGroup\n\t\tmu      sync.Mutex\n\t\tsem     = make(chan struct{}, maxConcurrency)\n\t)\n\n\tdoc.Find(\".item-jx.item-blog\").Each(func(_ int, item *goquery.Selection) {\n\t\ttitleSel := item.Find(\".subtitle h5 a\")\n\t\ttitle := strings.TrimSpace(titleSel.Text())\n\t\tdetailURL, ok := titleSel.Attr(\"href\")","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/daishudj/daishudj.go#L124-L160","documentation":"searchImpl returns \"[%s] 搜索返回状态码\" when the search page responds with a status other than 200. The plugin scrapes HTML with goquery and only handles a 200 body, so any other status is reported with its numeric code.","triggerScenarios":"The search GET completes but resp.StatusCode != http.StatusOK: 404 for a moved search route, 403/429 from WAF or rate limiting, or 5xx server errors.","commonSituations":"Site added Cloudflare/anti-bot protection returning 403, aggressive search requests trigger 429, the WordPress-style ?s= search route changed, or the origin returns 502/504 under load.","solutions":["Log the status code; treat 403/429 as blocking and add cookies, referer, or realistic headers via setCommonHeaders.","Back off and retry later for 429/5xx; reduce search request frequency.","If 404, verify the search URL format against the live site and update searchImpl.","Capture the response body on non-200 to identify WAF challenge pages."],"exampleFix":"// before\nif resp.StatusCode != http.StatusOK {\n    return nil, fmt.Errorf(\"[%s] 搜索返回状态码: %d\", p.Name(), resp.StatusCode)\n}\n// after\nif resp.StatusCode != http.StatusOK {\n    return nil, fmt.Errorf(\"[%s] 搜索返回状态码: %d (blocked=%v)\",\n        p.Name(), resp.StatusCode, resp.StatusCode == 403 || resp.StatusCode == 429)\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"results, err := plugin.Search(ctx, keyword)\nif err != nil && strings.Contains(err.Error(), \"搜索返回状态码\") {\n    var sc int\n    fmt.Sscanf(err.Error(), \"搜索返回状态码: %d\", &sc)\n    if sc == 429 || sc == 403 {\n        // back off significantly before the next attempt\n    }\n    return nil, err\n}","preventionTips":["Throttle search requests to avoid 429 rate limits.","Send realistic headers (User-Agent, Referer, cookies) to pass WAF checks.","Alert on recurring non-200 statuses — usually a site-side change, not a caller bug."],"tags":["http","status-code","go","scraping"],"backgroundTag":"http-non-200-response","analyzedSha":"beaa56133755a548ebc51b090b3816e2ae044aa6","analyzedAt":"2026-09-07T00:31:18.025Z","contentChangedAt":"2026-09-07T00:31:18.025Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}