{"record":{"id":"0deb7a760b6c70aa","repo":"fish2018/pansou","slug":"s-w-0deb7a","errorCode":null,"errorMessage":"[%s] 解析搜索页面失败: %w","messagePattern":"\\[(.+?)\\] 解析搜索页面失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/alupan/alupan.go","lineNumber":143,"sourceCode":"\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 创建请求失败: %w\", p.Name(), err)\n\t}\n\n\tsetCommonHeaders(req, \"https://www.aliupan.com/\")\n\n\tresp, err := p.doRequestWithRetry(req, client, searchMaxRetries)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 搜索请求失败: %w\", p.Name(), err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, 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 nil, fmt.Errorf(\"[%s] 解析搜索页面失败: %w\", p.Name(), err)\n\t}\n\n\tvar (\n\t\tresults []model.SearchResult\n\t\twg      sync.WaitGroup\n\t\tmu      sync.Mutex\n\t\tsem     = make(chan struct{}, maxConcurrency)\n\t)\n\n\tdoc.Find(\"article.excerpt\").Each(func(_ int, item *goquery.Selection) {\n\t\ttitleSel := item.Find(\"header h2 a\")\n\t\ttitle := strings.TrimSpace(titleSel.Text())\n\t\tdetailURL, ok := titleSel.Attr(\"href\")\n\t\tif !ok || title == \"\" || detailURL == \"\" {\n\t\t\treturn\n\t\t}\n\n\t\tarticleID := extractArticleID(detailURL)","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/alupan/alupan.go#L125-L161","documentation":"searchImpl wraps errors from goquery.NewDocumentFromReader, which parses the search response body as an HTML document. The response reached status 200 but goquery (built on golang.org/x/net/html) could not parse the body — the content is not valid HTML, is compressed/unreadable, or the body is truncated/empty due to an interrupted transfer.","triggerScenarios":"NewDocumentFromReader returns an error when the response body contains malformed HTML that the html parser rejects, or when reading the body fails mid-stream (connection reset during body read, gzip/deflate corruption, chunked encoding truncated).","commonSituations":"The site returns a 200 page that is actually a JSON error, a CAPTCHA/challenge page with broken markup, or binary content; a proxy or antivirus mangles the body; the server closes the connection mid-response; custom Transport settings interfere with automatic gzip handling.","solutions":["Capture the first bytes of the response body (Content-Type plus a snippet) when this error occurs to see what was actually returned.","Check the Content-Type header before parsing — skip or log when it is not text/html.","Inspect the raw body with curl to determine whether the site changed its markup or serves challenge pages on 200.","Check for mid-stream read errors (connection reset) in the wrapped error and rely on doRequestWithRetry for such transient failures.","Ensure the Transport isn't disabling automatic decompression (DisableCompression) while the server sends gzip."],"exampleFix":"// before\ndoc, err := goquery.NewDocumentFromReader(resp.Body)\nif err != nil {\n    return nil, fmt.Errorf(\"[%s] 解析搜索页面失败: %w\", p.Name(), err)\n}\n// after\nif ct := resp.Header.Get(\"Content-Type\"); !strings.Contains(ct, \"text/html\") {\n    return nil, fmt.Errorf(\"[%s] 非HTML响应: %s\", p.Name(), ct)\n}\ndoc, err := goquery.NewDocumentFromReader(resp.Body)\nif err != nil {\n    return nil, fmt.Errorf(\"[%s] 解析搜索页面失败: %w\", p.Name(), err)\n}","handlingStrategy":"validation","validationCode":"// Validate the response is parseable HTML before goquery\nct := resp.Header.Get(\"Content-Type\")\nif !strings.Contains(ct, \"text/html\") {\n    return nil, fmt.Errorf(\"unexpected content-type: %s\", ct)\n}\nhead, _ := io.ReadAll(io.LimitReader(resp.Body, 512))\nif !strings.Contains(string(head), \"<\") {\n    return nil, fmt.Errorf(\"response body is not HTML\")\n}","typeGuard":null,"tryCatchPattern":"// Go: wrap parse failure with body context for diagnosis\ndoc, err := goquery.NewDocumentFromReader(resp.Body)\nif err != nil {\n    return nil, fmt.Errorf(\"parse search page (status=%d, ct=%s): %w\",\n        resp.StatusCode, resp.Header.Get(\"Content-Type\"), err)\n}","preventionTips":["Check Content-Type before parsing every scraped response.","Log a body snippet on parse failure to spot CAPTCHA/challenge pages served with 200.","Confirm gzip handling: leave Transport compression enabled when the server sends gzip.","Watch for connection resets during body reads; treat them as retryable transport errors."],"tags":["html","parsing","goquery","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"}