{"record":{"id":"a239211e273f05e4","repo":"fish2018/pansou","slug":"s-formhash-w","errorCode":null,"errorMessage":"[%s] 获取formhash失败: %w","messagePattern":"\\[(.+?)\\] 获取formhash失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/qupanshe/qupanshe.go","lineNumber":79,"sourceCode":"\t}\n\n\t// 创建带有Cookie管理的专用客户端，确保整个搜索过程使用同一个session\n\tsessionClient, err := p.createSessionClient(client)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 创建session客户端失败: %w\", p.Name(), err)\n\t}\n\n\tif DebugLog {\n\t\tfmt.Printf(\"[qupanshe] 创建session客户端成功，开始三步搜索流程\\n\")\n\t}\n\n\t// Step 1: 获取首页formhash（使用session客户端）\n\tformhash, err := p.getFormhash(sessionClient)\n\tif err != nil {\n\t\tif DebugLog {\n\t\t\tfmt.Printf(\"[qupanshe] 获取formhash失败: %v\\n\", err)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"[%s] 获取formhash失败: %w\", p.Name(), err)\n\t}\n\tif DebugLog {\n\t\tfmt.Printf(\"[qupanshe] 获取到formhash: %s\\n\", formhash)\n\t}\n\n\t// Step 2: POST请求获取搜索结果URL（使用同一个session客户端）\n\tsearchURL, err := p.postSearchRequest(sessionClient, keyword, formhash)\n\tif err != nil {\n\t\tif DebugLog {\n\t\t\tfmt.Printf(\"[qupanshe] POST搜索请求失败: %v\\n\", err)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"[%s] POST搜索请求失败: %w\", p.Name(), err)\n\t}\n\tif DebugLog {\n\t\tfmt.Printf(\"[qupanshe] 获取搜索URL成功: %s\\n\", searchURL)\n\t}\n\n\t// Step 3: GET请求获取搜索结果（使用同一个session客户端）","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/qupanshe/qupanshe.go#L61-L97","documentation":"searchImpl wraps any failure from getFormhash, which GETs the site homepage and parses input[name='formhash'] to obtain the CSRF token needed for the search POST. This error means the homepage request or its processing failed (network, non-200 status, gzip or parse error, or missing formhash).","triggerScenarios":"p.getFormhash(sessionClient) fails during Step 1 of the three-step search flow: GET to BaseURL failed, non-200 status returned, gzip decoding or goquery parsing failed, or no formhash input was found in the HTML.","commonSituations":"Site is down or blocking the client (rate limiting, missing/blocked User-Agent), network outage or proxy issues, site redesign changing the formhash input markup, or a CDN returning an interstitial/challenge page instead of the real homepage.","solutions":["Enable DebugLog to see the actual status code and cookies received","Check network/proxy connectivity to BaseURL and that the site is up","Verify the User-Agent/headers in setRequestHeaders are not blocked by the site (try a browser UA)","Re-check the formhash selector against current site HTML and update if markup changed","Handle gzip/Cookie flow correctly — reuse the session client so cookies persist"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// pre-check connectivity before search\nresp, err := http.Head(BaseURL)\nif err != nil || resp.StatusCode != http.StatusOK {\n    // skip search, warn user site unreachable\n}","typeGuard":null,"tryCatchPattern":"results, err := plugin.Search(ctx, keyword)\nif err != nil {\n    var wrapped interface{ Unwrap() error }\n    if errors.As(err, new(error)) && strings.Contains(err.Error(), \"获取formhash失败\") {\n        time.Sleep(2*time.Second)\n        return plugin.Search(ctx, keyword) // bounded retry\n    }\n    return err\n}","preventionTips":["Keep DebugLog enabled in staging to see status codes and cookies","Always reuse the same session client across the search steps","Use a realistic browser User-Agent","Monitor for site markup changes and update selectors promptly","Add bounded retries with backoff"],"tags":["go","scraping","csrf","http"],"backgroundTag":"http-request-failed","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"}