{"record":{"id":"ee23bc2e09c3bace","repo":"fish2018/pansou","slug":"s-w-ee23bc","errorCode":null,"errorMessage":"[%s] 解析详情页失败: %w","messagePattern":"\\[(.+?)\\] 解析详情页失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/mizixing/mizixing.go","lineNumber":259,"sourceCode":"\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, detailURL, nil)\n\tif err != nil {\n\t\treturn detailData{}, fmt.Errorf(\"[%s] 创建详情页请求失败: %w\", p.Name(), err)\n\t}\n\tsetHTMLHeaders(req, detailURL)\n\n\tresp, err := p.doRequestWithRetry(req, client, maxRequestRetries)\n\tif err != nil {\n\t\treturn detailData{}, err\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn detailData{}, fmt.Errorf(\"[%s] 详情页返回状态码: %d\", p.Name(), resp.StatusCode)\n\t}\n\n\tdoc, err := goquery.NewDocumentFromReader(resp.Body)\n\tif err != nil {\n\t\treturn detailData{}, fmt.Errorf(\"[%s] 解析详情页失败: %w\", p.Name(), err)\n\t}\n\n\tcontent := doc.Find(\"article.article-content\")\n\tif content.Length() == 0 {\n\t\tcontent = doc.Find(\".article-content\")\n\t}\n\tif content.Length() == 0 {\n\t\tcontent = doc.Find(\".entry-content\")\n\t}\n\tif content.Length() == 0 {\n\t\tcontent = doc.Selection\n\t}\n\n\tcontent.Find(\"script, style, .bdsharebuttonbox, #respond, .post-views, .share, .relates\").Remove()\n\n\tlinks := extractLinksFromSelection(content)\n\n\tdescription := strings.TrimSpace(doc.Find(\"meta[name='description']\").AttrOr(\"content\", \"\"))","sourceCodeStart":241,"sourceCodeEnd":277,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/mizixing/mizixing.go#L241-L277","documentation":"Raised in fetchDetailData when goquery.NewDocumentFromReader fails to parse the HTTP response body as an HTML document. This means the body was malformed, truncated, or not HTML at all (e.g. a JSON error payload, gzip issue, or anti-bot interstitial page).","triggerScenarios":"Triggered when the detail page response body cannot be tokenized into an HTML document: empty body, invalid encoding, corrupted/compressed content read incorrectly, or the server returned an error page goquery cannot parse.","commonSituations":"Server returns a compressed body without the client handling Content-Encoding; response body already consumed before parsing; CDN serves a challenge/JS page; network interruption truncated the body mid-transfer.","solutions":["Check that resp.Body is not nil and has not been read before passing to NewDocumentFromReader","Verify the request disables or correctly handles compression (set Accept-Encoding explicitly or let http.Client auto-decompress)","Log a snippet of the raw body on failure to see what the server actually returned","Ensure the response is fully downloaded (no early ctx cancellation) before parsing","Fall back to a plain io.ReadAll check to confirm the content is HTML before goquery parsing"],"exampleFix":"// before\ndoc, err := goquery.NewDocumentFromReader(resp.Body)\nif err != nil {\n    return detailData{}, fmt.Errorf(\"[%s] 解析详情页失败: %w\", p.Name(), err)\n}\n// after\nbodyBytes, rerr := io.ReadAll(resp.Body)\nif rerr != nil {\n    return detailData{}, fmt.Errorf(\"[%s] 读取详情页失败: %w\", p.Name(), rerr)\n}\ndoc, err := goquery.NewDocumentFromReader(bytes.NewReader(bodyBytes))\nif err != nil {\n    return detailData{}, fmt.Errorf(\"[%s] 解析详情页失败: %w (body prefix: %.200s)\", p.Name(), err, string(bodyBytes))\n}","handlingStrategy":"validation","validationCode":"body, err := io.ReadAll(resp.Body)\nif err != nil { return err }\nct := resp.Header.Get(\"Content-Type\")\nif !strings.Contains(ct, \"text/html\") || len(bytes.TrimSpace(body)) == 0 {\n    return fmt.Errorf(\"unexpected response: content-type=%s len=%d\", ct, len(body))\n}\ndoc, err := goquery.NewDocumentFromReader(bytes.NewReader(body))","typeGuard":null,"tryCatchPattern":"doc, err := goquery.NewDocumentFromReader(resp.Body)\nif err != nil {\n    log.Printf(\"html parse failed: %v\", err)\n    return detailData{}, errParse\n}","preventionTips":["Always check Content-Type is text/html before parsing","Never read resp.Body twice","Log a body snippet on parse failure for diagnosis","Let http.Client auto-decompress; don't set raw Accept-Encoding"],"tags":["html-parsing","goquery","scraping","go"],"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"}