{"record":{"id":"7881309a39040dc6","repo":"fish2018/pansou","slug":"html-w-788130","errorCode":null,"errorMessage":"详情页HTML解析失败: %w","messagePattern":"详情页HTML解析失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/wuji/wuji.go","lineNumber":335,"sourceCode":"\t\treturn \"\", fmt.Errorf(\"详情页请求失败: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\t\n\t// 检查状态码\n\tif resp.StatusCode != 200 {\n\t\treturn \"\", fmt.Errorf(\"详情页返回状态码: %d\", resp.StatusCode)\n\t}\n\t\n\t// 读取响应体内容\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"读取详情页响应失败: %w\", err)\n\t}\n\t\n\t// 解析HTML\n\tdoc, err := goquery.NewDocumentFromReader(strings.NewReader(string(body)))\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"详情页HTML解析失败: %w\", err)\n\t}\n\t\n\t// 提取磁力链接\n\tmagnetInput := doc.Find(\"input#input-magnet\")\n\tif magnetInput.Length() == 0 {\n\t\treturn \"\", fmt.Errorf(\"未找到磁力链接输入框\")\n\t}\n\t\n\tmagnetLink, exists := magnetInput.Attr(\"value\")\n\tif !exists || magnetLink == \"\" {\n\t\treturn \"\", fmt.Errorf(\"磁力链接为空\")\n\t}\n\t\n\t// 存入缓存\n\tmagnetCache.Store(detailURL, magnetCacheEntry{\n\t\tMagnetLink: magnetLink,\n\t\tTimestamp:  time.Now(),\n\t})","sourceCodeStart":317,"sourceCodeEnd":353,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/wuji/wuji.go#L317-L353","documentation":"The downloaded detail-page HTML is parsed with goquery.NewDocumentFromReader. goquery's HTML parser (go.net/html) almost never errors on real input, so this error indicates malformed input such as a nil reader, or an empty/invalid body stream failing to parse. When it fires, the wrapped error comes from the underlying html parser.","triggerScenarios":"goquery.NewDocumentFromReader(strings.NewReader(string(body))) returns an error — practically only when body is produced by a failing/nil reader or the parse hits a fatal reader error. With an in-memory strings.Reader over already-read bytes this is an edge case.","commonSituations":"A code path change passing a nil or already-closed body reader; extremely truncated responses reused downstream; custom modifications to the function that stream the body directly into goquery while the network stream errors.","solutions":["Verify body is fully read before parsing (io.ReadAll succeeded) and non-empty; skip empty bodies earlier.","If streaming resp.Body into goquery directly, close/replace that path — read fully first so parse errors are separated from network errors.","Check the wrapped parse error via %w unwrap; go.net/html parse errors usually mean the input reader failed, not the HTML content.","Add a guard: if len(body) == 0 return a clearer 'empty detail page' error before parsing."],"exampleFix":"// before\ndoc, err := goquery.NewDocumentFromReader(strings.NewReader(string(body)))\nif err != nil {\n    return \"\", fmt.Errorf(\"详情页HTML解析失败: %w\", err)\n}\n// after\nif len(bytes.TrimSpace(body)) == 0 {\n    return \"\", fmt.Errorf(\"详情页响应为空: %s\", detailURL)\n}\ndoc, err := goquery.NewDocumentFromReader(strings.NewReader(string(body)))\nif err != nil {\n    return \"\", fmt.Errorf(\"详情页HTML解析失败: %w\", err)\n}","handlingStrategy":"validation","validationCode":"if len(bytes.TrimSpace(body)) == 0 {\n    return fmt.Errorf(\"empty detail page body\")\n}\n// safe to parse\ndoc, err := goquery.NewDocumentFromReader(strings.NewReader(string(body)))","typeGuard":null,"tryCatchPattern":"doc, err := goquery.NewDocumentFromReader(strings.NewReader(string(body)))\nif err != nil {\n    return fmt.Errorf(\"parse failed: %w\", err)\n}","preventionTips":["Read the body fully with io.ReadAll before parsing","Reject empty bodies before handing them to the parser","Never stream a live network body straight into goquery"],"tags":["html","parsing","scraping","goquery"],"backgroundTag":"invalid-json-response","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"}