{"record":{"id":"27271fd33cf5b14b","repo":"fish2018/pansou","slug":"formhash","errorCode":null,"errorMessage":"未找到formhash值","messagePattern":"未找到formhash值","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/qupanshe/qupanshe.go","lineNumber":211,"sourceCode":"\n\t// 查找formhash\n\tformhash := \"\"\n\tinputCount := doc.Find(\"input[name='formhash']\").Length()\n\tif DebugLog {\n\t\tfmt.Printf(\"[qupanshe] 找到input[name='formhash']元素数量: %d\\n\", inputCount)\n\t}\n\n\tdoc.Find(\"input[name='formhash']\").Each(func(i int, s *goquery.Selection) {\n\t\tif value, exists := s.Attr(\"value\"); exists && value != \"\" {\n\t\t\tformhash = value\n\t\t\tif DebugLog {\n\t\t\t\tfmt.Printf(\"[qupanshe] 找到formhash[%d]: %s\\n\", i, value)\n\t\t\t}\n\t\t}\n\t})\n\n\tif formhash == \"\" {\n\t\treturn \"\", fmt.Errorf(\"未找到formhash值\")\n\t}\n\n\treturn formhash, nil\n}\n\n// postSearchRequest 发送POST请求获取搜索结果URL\nfunc (p *QupanshePlugin) postSearchRequest(client *http.Client, keyword, formhash string) (string, error) {\n\t// 添加延时，避免请求过快\n\ttime.Sleep(2 * time.Second)\n\n\t// 构建POST请求\n\tsearchURL := fmt.Sprintf(\"%s/search.php?mod=forum\", BaseURL)\n\tdata := url.Values{}\n\tdata.Set(\"formhash\", formhash)\n\tdata.Set(\"srchtxt\", keyword)\n\tdata.Set(\"searchsubmit\", \"yes\")\n\n\tctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)","sourceCodeStart":193,"sourceCodeEnd":229,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/qupanshe/qupanshe.go#L193-L229","documentation":"getFormhash scrapes the site's HTML to extract the anti-CSRF 'formhash' value required by the search form. If, after parsing the document with goquery, no formhash value is found in any input field or URL, it returns this error. It means the fetched page did not contain the expected hidden form token.","triggerScenarios":"The GET request for the search page returned a login page, captcha page, Cloudflare challenge, or error page instead of the normal page containing <input name=\"formhash\">; or the site's markup changed so the goquery selector no longer matches.","commonSituations":"Site is blocking the plugin's default User-Agent/headers; the target site changed its HTML template; the server redirected to an anti-bot interstitial; the site requires cookies/login to render the search form.","solutions":["Log the fetched HTML (enable DebugLog or dump resp body) to see what page was actually returned","Set/refresh realistic request headers (User-Agent, Referer, cookies) in setRequestHeaders so the site serves the real page","Update the formhash selector in getFormhash to match the current site markup","Add retry logic — the page may load correctly on a second attempt","Check whether the site now requires login or JS challenge solving"],"exampleFix":"// before\nif formhash == \"\" {\n    return \"\", fmt.Errorf(\"未找到formhash值\")\n}\n// after\nif formhash == \"\" {\n    return \"\", fmt.Errorf(\"未找到formhash值 (page status body snippet: %.200s)\", pageBody)\n}","handlingStrategy":"retry","validationCode":"// Before trusting the flow, verify the fetched page contains a formhash input\n// (getFormhash's goquery scope) — e.g. in DebugLog:\n// doc.Find(\"input[name='formhash']\").Each(func(i int, s *goquery.Selection) {\n//     fmt.Println(\"formhash:\", s.AttrOr(\"value\", \"\"))\n// })\nif err != nil { /* handle fetch failure */ }","typeGuard":"func hasFormhash(doc *goquery.Document) bool {\n    return doc.Find(\"input[name='formhash']\").Length() > 0\n}","tryCatchPattern":"formhash, err := plugin.GetFormhash(ctx)\nif err != nil {\n    if strings.Contains(err.Error(), \"未找到formhash值\") {\n        // page shape changed or was a block page — refresh cookies/headers and retry once\n        return plugin.GetFormhash(ctx)\n    }\n    return err\n}","preventionTips":["Send realistic browser User-Agent and Referer headers to avoid block pages","Log the raw HTML when formhash is empty to diagnose markup drift","Periodically re-verify the site's search page structure","Handle login-walls/captchas explicitly instead of blind retries"],"tags":["scraping","html-parsing","csrf-token","http"],"backgroundTag":"unexpected-response-shape","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"}