{"record":{"id":"920d2be5f795f9d8","repo":"fish2018/pansou","slug":"s-d-w-920d2b","errorCode":null,"errorMessage":"[%s] 第%d页解析失败: %w","messagePattern":"\\[(.+?)\\] 第(.+?)页解析失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/xiaoyu/xiaoyu.go","lineNumber":164,"sourceCode":"\trequestURL := fmt.Sprintf(searchURL, page, url.PathEscape(keyword))\n\tctx, cancel := context.WithTimeout(context.Background(), requestTimeout)\n\tdefer cancel()\n\n\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, requestURL, nil)\n\tif err != nil {\n\t\treturn pageResult{}, fmt.Errorf(\"[%s] 创建第%d页请求失败: %w\", p.Name(), page, err)\n\t}\n\tsetRequestHeaders(req)\n\n\tresp, err := doRequestWithRetry(client, req)\n\tif err != nil {\n\t\treturn pageResult{}, fmt.Errorf(\"[%s] 第%d页搜索请求失败: %w\", p.Name(), page, err)\n\t}\n\tdefer resp.Body.Close()\n\n\tdoc, err := goquery.NewDocumentFromReader(io.LimitReader(resp.Body, maxResponseSize))\n\tif err != nil {\n\t\treturn pageResult{}, fmt.Errorf(\"[%s] 第%d页解析失败: %w\", p.Name(), page, err)\n\t}\n\n\treturn parsePage(doc), nil\n}\n\nfunc setRequestHeaders(req *http.Request) {\n\treq.Header.Set(\"User-Agent\", \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36\")\n\treq.Header.Set(\"Accept\", \"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\")\n\treq.Header.Set(\"Accept-Language\", \"zh-CN,zh;q=0.9,en;q=0.8\")\n\treq.Header.Set(\"Connection\", \"keep-alive\")\n\treq.Header.Set(\"Referer\", baseURL+\"/\")\n}\n\nfunc doRequestWithRetry(client *http.Client, req *http.Request) (*http.Response, error) {\n\tvar lastErr error\n\tfor attempt := 0; attempt <= maxRetries; attempt++ {\n\t\tif attempt > 0 {\n\t\t\ttime.Sleep(time.Duration(attempt) * 200 * time.Millisecond)","sourceCodeStart":146,"sourceCodeEnd":182,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/xiaoyu/xiaoyu.go#L146-L182","documentation":"After a successful response, fetchPage parses the (size-limited) body into a goquery document. If goquery.NewDocumentFromReader fails, the body was not valid parseable HTML (or an I/O error occurred while reading), and the error is wrapped with plugin name and page number.","triggerScenarios":"The response body, limited to maxResponseSize by io.LimitReader, is not parseable as HTML — e.g. it is a JSON error payload, gzip/charset mismatch, or truncated mid-tag by the size cap.","commonSituations":"Server returned JSON or plaintext error instead of HTML; response truncated exactly by maxResponseSize leaving broken markup the parser chokes on; Content-Encoding not transparently decoded by the transport.","solutions":["Log the first bytes of the response body and the Content-Type header to see what was actually returned.","Raise maxResponseSize if valid pages exceed the current cap and are being truncated mid-document.","Ensure the HTTP transport decodes gzip (check Accept-Encoding handling) before parsing.","Check status code before parsing so error payloads are not fed to goquery."],"exampleFix":"// before\ndoc, err := goquery.NewDocumentFromReader(io.LimitReader(resp.Body, maxResponseSize))\nif err != nil {\n\treturn pageResult{}, fmt.Errorf(\"[%s] 第%d页解析失败: %w\", p.Name(), page, err)\n}\n// after\nct := resp.Header.Get(\"Content-Type\")\ndoc, err := goquery.NewDocumentFromReader(io.LimitReader(resp.Body, maxResponseSize))\nif err != nil {\n\treturn pageResult{}, fmt.Errorf(\"[%s] 第%d页解析失败 (status=%d content-type=%s): %w\", p.Name(), page, resp.StatusCode, ct, err)\n}","handlingStrategy":"validation","validationCode":"ct := resp.Header.Get(\"Content-Type\")\nif !strings.Contains(ct, \"text/html\") {\n\treturn fmt.Errorf(\"unexpected content-type %q; skip parsing\", ct)\n}","typeGuard":"func isHTMLResponse(resp *http.Response) bool { return strings.Contains(resp.Header.Get(\"Content-Type\"), \"text/html\") }","tryCatchPattern":"doc, err := goquery.NewDocumentFromReader(io.LimitReader(resp.Body, maxResponseSize))\nif err != nil {\n\tlog.Printf(\"page %d unparseable (status=%d ct=%s)\", page, resp.StatusCode, resp.Header.Get(\"Content-Type\"))\n\treturn pageResult{}, err\n}","preventionTips":["Verify status 200 and HTML content type before parsing.","Keep maxResponseSize above typical page sizes to avoid mid-tag truncation.","Let the HTTP transport handle gzip decoding rather than parsing compressed bytes."],"tags":["html-parsing","goquery","scraping"],"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"}