{"record":{"id":"7babe6734dd5ac0f","repo":"fish2018/pansou","slug":"s-w-7babe6","errorCode":null,"errorMessage":"[%s] 解析搜索页面失败: %w","messagePattern":"\\[(.+?)\\] 解析搜索页面失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/labi/labi.go","lineNumber":210,"sourceCode":"\treq.Header.Set(\"Upgrade-Insecure-Requests\", \"1\")\n\treq.Header.Set(\"Cache-Control\", \"max-age=0\")\n\treq.Header.Set(\"Referer\", strings.TrimRight(baseURL, \"/\")+\"/\")\n\n\t// 5. 发送请求（带重试机制）\n\tresp, err := p.doRequestWithRetry(req, client)\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 != 200 {\n\t\treturn nil, fmt.Errorf(\"[%s] 搜索请求返回状态码: %d\", p.Name(), resp.StatusCode)\n\t}\n\n\t// 3. 解析搜索结果页面\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\t// 4. 提取搜索结果\n\tvar results []model.SearchResult\n\n\tdoc.Find(\".module-search-item\").Each(func(i int, s *goquery.Selection) {\n\t\tresult := p.parseSearchItem(s, keyword)\n\t\tif result.UniqueID != \"\" {\n\t\t\tresults = append(results, result)\n\t\t}\n\t})\n\n\treturn results, nil\n}\n\n// parseSearchItem 解析单个搜索结果项\nfunc (p *LabiAsyncPlugin) parseSearchItem(s *goquery.Selection, keyword string) model.SearchResult {\n\tresult := model.SearchResult{}","sourceCodeStart":192,"sourceCodeEnd":228,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/labi/labi.go#L192-L228","documentation":"searchAtBase wraps an error from goquery.NewDocumentFromReader: the labi mirror returned a 200 status but its body could not be parsed as HTML. The response was likely empty, truncated, compressed, or not HTML (e.g. a challenge or JSON error body).","triggerScenarios":"goquery.NewDocumentFromReader(resp.Body) errors after a 200 response: empty/short body from a broken mirror, manually-set Accept-Encoding causing raw gzip to reach the parser, or a non-HTML body.","commonSituations":"A half-dead mirror returning empty 200 pages, proxy/CDN mangling or truncating responses, custom transport without auto-decompression, anti-bot challenge served with a 200 status.","solutions":["Inspect the wrapped parse error and dump the first bytes of the body to see what was actually returned","Do not set Accept-Encoding manually so net/http transparently decompresses before goquery reads it","Treat this mirror as unhealthy and fall back to the next candidate base URL","Retry once on short/truncated bodies before giving up on the mirror"],"exampleFix":"// before\nreq.Header.Set(\"Accept-Encoding\", \"gzip\")\n// after\n// remove the header; let net/http handle decompression so goquery receives plain HTML","handlingStrategy":"validation","validationCode":"resp, err := client.Get(searchURL)\nif err == nil {\n    defer resp.Body.Close()\n    if ct := resp.Header.Get(\"Content-Type\"); !strings.Contains(ct, \"text/html\") {\n        // parse will fail; treat mirror as unhealthy\n    }\n}","typeGuard":"func looksLikeHTML(b []byte) bool {\n    s := strings.TrimSpace(strings.ToLower(string(b[:min(len(b), 512)])))\n    return strings.HasPrefix(s, \"<!doctype html\") || strings.HasPrefix(s, \"<html\")\n}","tryCatchPattern":"doc, err := searchAndParse(base, kw)\nif err != nil && strings.Contains(err.Error(), \"解析搜索页面失败\") {\n    log.Printf(\"mirror %s returned non-HTML body: %v\", base, err)\n    return tryNextMirror(kw)\n}","preventionTips":["Don't set Accept-Encoding manually; ensure bodies arrive decompressed","Verify Content-Type and body prefix before goquery parsing","Quarantine mirrors that repeatedly serve empty/challenge 200 bodies","Retest mirrors after proxy or CDN changes that can alter bodies"],"tags":["html","parsing","goquery","scraping"],"backgroundTag":"invalid-html-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"}