{"record":{"id":"931d180aa6f62f73","repo":"fish2018/pansou","slug":"js","errorCode":null,"errorMessage":"未找到JS文件","messagePattern":"未找到JS文件","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/panyq/panyq.go","lineNumber":687,"sourceCode":"\t\tif err != nil {\n\t\t\t// 如果连响应体都读取失败，则返回状态码错误并附上读取错误\n\t\t\treturn nil, fmt.Errorf(\"请求失败，状态码: %d，且读取响应体错误: %v\", resp.StatusCode, err)\n\t\t}\n\t\t// 将更详细的状态信息 (如 \"404 Not Found\") 和响应体内容一起作为错误返回\n\t\treturn nil, fmt.Errorf(\"请求失败，状态: %s, 详情: %s\", resp.Status, string(bodyBytes))\n\t}\n\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"读取响应失败: %w\", err)\n\t}\n\t\n\t// 提取JS文件路径\n\tjsRegex := regexp.MustCompile(`<script src=\"(/_next/static/[^\"]+\\.js)\"`)\n\tmatches := jsRegex.FindAllStringSubmatch(string(body), -1)\n\t\n\tif len(matches) == 0 {\n\t\treturn nil, fmt.Errorf(\"未找到JS文件\")\n\t}\n\t\n\t// 收集所有潜在的Action ID\n\tidSet := make(map[string]struct{})\n\tidRegex := regexp.MustCompile(`[\"\\']([a-f0-9]{40})[\"\\']{1}`)\n\t\n\tfor _, match := range matches {\n\t\tjsURL := BaseURL + match[1]\n\t\t\n\t\t// 创建JS文件请求\n\t\tjsReq, err := http.NewRequest(\"GET\", jsURL, nil)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\t\n\t\t// 设置JS文件请求头，保持与首页请求一致\n\t\tjsReq.Header.Set(\"Referer\", BaseURL)\n\t\tjsReq.Header.Set(\"Origin\", BaseURL)","sourceCodeStart":669,"sourceCodeEnd":705,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/panyq/panyq.go#L669-L705","documentation":"The homepage was fetched successfully (HTTP 200, body read), but the regular expression `<script src=\"(/_next/static/[^\"]+\\.js)\"` found no matches in the HTML. The discovery step depends on Next.js static script tags being present in the raw HTML; their absence means the page structure changed, the page is a client-rendered shell, or an anti-bot interstitial was served instead of the real homepage.","triggerScenarios":"jsRegex.FindAllStringSubmatch returns zero matches on the fetched homepage HTML inside findPotentialActionIDs — page no longer uses the expected <script src=\"/_next/static/*.js\"> tags, or the response is a challenge/consent page rather than the app HTML.","commonSituations":"The site upgraded Next.js or switched frameworks so scripts moved to different paths or are injected at runtime; Cloudflare served a challenge page with 200 OK; the site now renders via SSR streaming without those exact tags; regex broke due to attribute order/quotes changing (e.g. src=\"https://cdn.../_next/static/...\").","solutions":["Dump the fetched HTML (log string(body)) and inspect what actually came back — determine whether it's a challenge page or a restructured app shell.","Relax/update the regex: allow absolute URLs and other attribute orders, e.g. `<script[^>]+src=\"[^\"]*?/_next/static/[^\"]+\\.js\"`.","If scripts are runtime-injected, fetch the build manifest directly (/_next/static/<buildId>/_buildManifest.js) or the __NEXT_DATA__ JSON instead of scraping script tags.","Check whether the site changed frameworks entirely and update the discovery strategy in findPotentialActionIDs.","Ensure an anti-bot challenge isn't the cause — a 200 HTML page containing 'cf-challenge' or similar means you need a browser-like fetch, not a regex change."],"exampleFix":"// before\njsRegex := regexp.MustCompile(`<script src=\"(/_next/static/[^\"]+\\.js)\"`)\n// after — tolerate absolute URLs and attribute order\njsRegex := regexp.MustCompile(`<script[^>]+src=\"([^\"]*?/_next/static/[^\"]+\\.js)\"`)","handlingStrategy":"fallback","validationCode":null,"typeGuard":"func looksLikeAppHTML(body []byte) bool {\n    s := string(body)\n    if strings.Contains(s, \"cf-challenge\") || strings.Contains(s, \"Just a moment\") {\n        return false // anti-bot interstitial\n    }\n    return strings.Contains(s, \"_next/static\") || strings.Contains(s, \"__NEXT_DATA__\")\n}","tryCatchPattern":"ids, err := findPotentialActionIDs(client, homeURL)\nif err != nil {\n    if err.Error() == \"未找到JS文件\" {\n        // fallback: fetch the Next.js build manifest instead\n        return findActionIDsFromBuildManifest(client, homeURL)\n    }\n    return nil, err\n}","preventionTips":["Log a snippet of fetched HTML when extraction fails so layout changes are caught early.","Keep the regex tolerant of absolute script URLs and attribute ordering.","Add a secondary extraction path (build manifest / __NEXT_DATA__) as a fallback.","Detect challenge pages (200 OK + challenge markers) before assuming a layout change."],"tags":["scraping","regex","nextjs","go"],"backgroundTag":"empty-result-set","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"}