{"record":{"id":"beb9a9871a8fc41d","repo":"fish2018/pansou","slug":"url","errorCode":null,"errorMessage":"未获取到重定向URL","messagePattern":"未获取到重定向URL","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/panwiki/panwiki.go","lineNumber":207,"sourceCode":"\t\t\tp.setRequestHeaders(req)\n\t\t\t\n\t\t\tresp, err = client.Do(req)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"备用域名请求也失败: %w\", err)\n\t\t\t}\n\t\t} else {\n\t\t\treturn nil, fmt.Errorf(\"初始请求失败: %w\", err)\n\t\t}\n\t}\n\tdefer resp.Body.Close()\n\t\n\t// 重置重定向策略\n\tclient.CheckRedirect = nil\n\t\n\t// 获取重定向URL\n\tlocation := resp.Header.Get(\"Location\")\n\tif location == \"\" {\n\t\treturn nil, fmt.Errorf(\"未获取到重定向URL\")\n\t}\n\t\n\t// 构建完整的重定向URL\n\tvar searchURL string\n\tif strings.HasPrefix(location, \"http\") {\n\t\tsearchURL = location\n\t} else {\n\t\tsearchURL = p.currentBaseURL + \"/\" + strings.TrimPrefix(location, \"/\")\n\t}\n\t\n\t// 如果不是第一页，修改URL中的page参数\n\tif page > 1 {\n\t\tif strings.Contains(searchURL, \"searchid=\") {\n\t\t\t// 提取searchid并构建分页URL\n\t\t\tre := regexp.MustCompile(`searchid=(\\d+)`)\n\t\t\tmatches := re.FindStringSubmatch(searchURL)\n\t\t\tif len(matches) > 1 {\n\t\t\t\tsearchid := matches[1]","sourceCodeStart":189,"sourceCodeEnd":225,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/panwiki/panwiki.go#L189-L225","documentation":"searchPage relies on the first request returning a 30x redirect whose Location header points to the real search-results URL. If the response carries no Location header — the site returned a normal page (200), an error page, or was redirect-handled differently — the plugin cannot proceed and throws \"未获取到重定向URL\".","triggerScenarios":"resp.Header.Get(\"Location\") is empty after the initial request. Happens when the Discuz forum stopped issuing the searchid redirect (e.g. serving results directly, requiring login, showing a captcha/anti-bot page, or returning 200 with an error), or when the site is reachable but behaves differently from what the scraper expects.","commonSituations":"Upstream site layout/flow changed (site update), anti-bot protection serving a 200 challenge page, search blocked for anonymous users requiring login, rate limiting returning a normal page instead of redirect.","solutions":["Log resp.StatusCode and a body snippet when Location is empty to see what the server actually returned.","Check if the site now requires login/captcha for search and update the plugin flow accordingly.","Verify the search URL format still matches the current Discuz redirect behavior (site version change).","Add retry with backoff in case of transient anti-bot challenges.","Update User-Agent/headers if the site began fingerprinting clients."],"exampleFix":"// before\nlocation := resp.Header.Get(\"Location\")\nif location == \"\" {\n    return nil, fmt.Errorf(\"未获取到重定向URL\")\n}\n// after\nlocation := resp.Header.Get(\"Location\")\nif location == \"\" {\n    return nil, fmt.Errorf(\"未获取到重定向URL (status=%d)\", resp.StatusCode)\n}","handlingStrategy":"fallback","validationCode":"// Go: sanity-check that the site still behaves as expected before relying on the flow\nresp, _ := http.Head(baseURL + \"/search.php\")\n// if StatusOK without redirect, the redirect-flow assumption is broken","typeGuard":null,"tryCatchPattern":"results, err := plugin.Search(keyword, page)\nif err != nil && strings.Contains(err.Error(), \"未获取到重定向URL\") {\n    log.Printf(\"panwiki flow changed (site update or anti-bot): %v\", err)\n    // switch to fallback plugin/source\n}","preventionTips":["Add integration tests asserting the redirect-flow still works.","Log status code and body head when Location is empty.","Watch for upstream site updates; pin a monitor on the search endpoint.","Treat repeated occurrences as a signal the scraper needs updating, not a transient fault."],"tags":["http","scraping","redirect","go"],"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"}