fish2018/pansou · error
未找到JS文件
Error message
未找到JS文件
What it means
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.
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.
Example fix
// before jsRegex := regexp.MustCompile(`<script src="(/_next/static/[^"]+\.js)"`) // after — tolerate absolute URLs and attribute order jsRegex := regexp.MustCompile(`<script[^>]+src="([^"]*?/_next/static/[^"]+\.js)"`)
Defensive patterns
Strategy: fallback
Type guard
func looksLikeAppHTML(body []byte) bool {
s := string(body)
if strings.Contains(s, "cf-challenge") || strings.Contains(s, "Just a moment") {
return false // anti-bot interstitial
}
return strings.Contains(s, "_next/static") || strings.Contains(s, "__NEXT_DATA__")
} Try / catch
ids, err := findPotentialActionIDs(client, homeURL)
if err != nil {
if err.Error() == "未找到JS文件" {
// fallback: fetch the Next.js build manifest instead
return findActionIDsFromBuildManifest(client, homeURL)
}
return nil, err
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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/...").
Understand the failure class
Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/931d180aa6f62f73.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/panyq/panyq.go:687
if err != nil {
// 如果连响应体都读取失败,则返回状态码错误并附上读取错误
return nil, fmt.Errorf("请求失败,状态码: %d,且读取响应体错误: %v", resp.StatusCode, err)
}
// 将更详细的状态信息 (如 "404 Not Found") 和响应体内容一起作为错误返回
return nil, fmt.Errorf("请求失败,状态: %s, 详情: %s", resp.Status, string(bodyBytes))
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("读取响应失败: %w", err)
}
// 提取JS文件路径
jsRegex := regexp.MustCompile(`<script src="(/_next/static/[^"]+\.js)"`)
matches := jsRegex.FindAllStringSubmatch(string(body), -1)
if len(matches) == 0 {
return nil, fmt.Errorf("未找到JS文件")
}
// 收集所有潜在的Action ID
idSet := make(map[string]struct{})
idRegex := regexp.MustCompile(`["\']([a-f0-9]{40})["\']{1}`)
for _, match := range matches {
jsURL := BaseURL + match[1]
// 创建JS文件请求
jsReq, err := http.NewRequest("GET", jsURL, nil)
if err != nil {
continue
}
// 设置JS文件请求头,保持与首页请求一致
jsReq.Header.Set("Referer", BaseURL)
jsReq.Header.Set("Origin", BaseURL)View on GitHub (pinned to beaa561337)