{"record":{"id":"74664f790df90180","repo":"fish2018/pansou","slug":"s-html-w-74664f","errorCode":null,"errorMessage":"[%s] HTML解析失败: %w","messagePattern":"\\[(.+?)\\] HTML解析失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/clmao/clmao.go","lineNumber":213,"sourceCode":"\t}\n\n\t// 读取响应体内容\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 读取响应失败: %w\", p.Name(), err)\n\t}\n\n\tdecodedHTML := decodeModernPayload(string(body))\n\tif decodedHTML != string(body) {\n\t\tif modernResults := p.parseModernSearchResults(client, decodedHTML); len(modernResults) > 0 {\n\t\t\treturn modernResults, nil\n\t\t}\n\t}\n\n\t// 兼容旧模板\n\tdoc, err := goquery.NewDocumentFromReader(strings.NewReader(decodedHTML))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] HTML解析失败: %w\", p.Name(), err)\n\t}\n\n\t// 提取搜索结果\n\treturn p.extractSearchResults(doc), nil\n}\n\nfunc decodeModernPayload(raw string) string {\n\tmatch := modernPayloadRegex.FindStringSubmatch(raw)\n\tif len(match) < 2 {\n\t\treturn raw\n\t}\n\tdecoded, err := base64.StdEncoding.DecodeString(match[1])\n\tif err != nil {\n\t\treturn raw\n\t}\n\ttext, err := url.PathUnescape(string(decoded))\n\tif err != nil {\n\t\treturn string(decoded)","sourceCodeStart":195,"sourceCodeEnd":231,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/clmao/clmao.go#L195-L231","documentation":"ClmaoPlugin.searchPage fails while parsing the decoded HTML with goquery.NewDocumentFromReader. goquery only errors when the underlying reader fails (its HTML parser is error-tolerant), so this usually indicates the decoded payload reader failed or an empty stream was handed in. Note: most real parse problems (no results found) do NOT produce this error — they surface as empty results from extractSearchResults.","triggerScenarios":"After decodeModernPayload produced decodedHTML, strings.NewReader(decodedHTML) is passed to goquery.NewDocumentFromReader and returns a non-nil err (reader/IO failure), so searchPage returns \"[clmao] HTML解析失败: %w\".","commonSituations":"decodeModernPayload returned corrupted/empty output on an unexpected page (e.g. a CAPTCHA or error page fed through the decoder); a bug in the decode step produced an invalid reader state; extremely large decoded payloads hitting memory limits.","solutions":["Log the first N bytes of decodedHTML to confirm it is actually search-results HTML and not a CAPTCHA/error page.","Check decodeModernPayload for edge cases that could corrupt or empty the payload (e.g. wrong base64/encoding assumptions after a site update).","Goquery itself rarely errors — verify the error comes from the reader, and treat empty decodedHTML as an empty-result case instead.","If the site changed markup so modern/legacy parsing both fail, update the selectors in parseModernSearchResults/extractSearchResults."],"exampleFix":"// before\ndoc, err := goquery.NewDocumentFromReader(strings.NewReader(decodedHTML))\nif err != nil {\n    return nil, fmt.Errorf(\"[%s] HTML解析失败: %w\", p.Name(), err)\n}\n// after\nif strings.TrimSpace(decodedHTML) == \"\" {\n    return nil, fmt.Errorf(\"[%s] 空响应，可能被反爬拦截\", p.Name())\n}\ndoc, err := goquery.NewDocumentFromReader(strings.NewReader(decodedHTML))\nif err != nil {\n    return nil, fmt.Errorf(\"[%s] HTML解析失败: %w\", p.Name(), err)\n}","handlingStrategy":"fallback","validationCode":"func looksLikeSearchResults(html string) bool {\n    return strings.Contains(html, \"result\") || strings.Contains(html, \"search\")\n}","typeGuard":null,"tryCatchPattern":"results, err := p.searchPage(client, keyword, page)\nif err != nil {\n    if strings.Contains(err.Error(), \"HTML解析失败\") {\n        // decoded payload was not parseable HTML — likely CAPTCHA or changed page\n        log.Printf(\"clmao returned non-HTML payload; possible anti-bot page\")\n        return nil, err\n    }\n    return nil, err\n}","preventionTips":["Verify decodeModernPayload output before parsing (non-empty, starts with <html or <!doctype).","Treat CAPTCHA/challenge pages as a distinct case, not a parse error.","Update selectors when the site redesigns; test parsing against a saved HTML fixture.","Log a body snippet on failure to speed diagnosis."],"tags":["html-parsing","goquery","scraping"],"backgroundTag":"unexpected-response-shape","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"}