fish2018/pansou · error
[ ] 提取搜索结果失败
Error message
[%s] 提取搜索结果失败: %w
What it means
After a successful body read, ash attempts to parse the embedded JSON out of the HTML page via extractResultsFromBytes. If extraction fails (page layout changed, no embedded data, malformed script block), searchImpl wraps the error with the plugin name. This is a page-structure/parse failure, not a network failure.
Solutions
- Dump the failing body (truncated) to inspect what HTML was actually returned.
- Update the extraction regex/logic to match the site's current markup.
- Detect challenge/error pages early (status 200 but challenge markers) and retry or rotate identity.
- Unit-test extractResultsFromBytes with a captured fixture whenever the site changes.
Example fix
// before
results, err := p.extractResultsFromBytes(body)
if err != nil {
return nil, fmt.Errorf("[%s] 提取搜索结果失败: %w", p.Name(), err)
}
// after
results, err := p.extractResultsFromBytes(body)
if err != nil {
log.Printf("[%s] extract failed, head=%q", p.Name(), body[:min(len(body),300)])
return nil, fmt.Errorf("[%s] 提取搜索结果失败: %w", p.Name(), err)
} Defensive patterns
Strategy: fallback
Validate before calling
if !bytes.Contains(body, []byte("window.__INITIAL")) { return fmt.Errorf("page structure changed; got %q", body[:200]) } Try / catch
results, err := p.extractResultsFromBytes(body)
if err != nil {
log.Printf("extract failed: %v; page head: %q", err, body[:min(len(body),300)])
return fallbackParse(body) // alternate extractor or empty result
} Prevention
- Pin extractor regexes against captured page fixtures
- Detect anti-bot challenge pages before extracting
- Test extraction after any known site update
- Fail soft (empty results) instead of hard error where acceptable
When it happens
Trigger: p.extractResultsFromBytes(body) returns an error because the expected embedded JSON cannot be located or cleaned from the HTML — ash.go:113. Note it also surfaces error 36 (JSON解析失败) wrapped through this path.
Common situations: The ASH site changed its HTML/JS structure so the regex no longer matches; the server returned an error/interstitial page with 200; anti-bot HTML challenge page instead of real content.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/f8c33d9058eab19e.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/ash/ash.go:113
defer resp.Body.Close()
// 检查状态码
if resp.StatusCode != 200 {
return nil, fmt.Errorf("[%s] 请求返回状态码: %d", p.Name(), resp.StatusCode)
}
// 读取响应(使用有限制的读取,避免读取过大内容)
// ASH页面通常不会太大,限制在2MB以内
limitReader := io.LimitReader(resp.Body, 2*1024*1024)
body, err := io.ReadAll(limitReader)
if err != nil {
return nil, fmt.Errorf("[%s] 读取响应失败: %w", p.Name(), err)
}
// 从HTML中提取JSON数据(直接传递字节,避免字符串转换)
results, err := p.extractResultsFromBytes(body)
if err != nil {
return nil, fmt.Errorf("[%s] 提取搜索结果失败: %w", p.Name(), err)
}
// 关键词过滤
filtered := plugin.FilterResultsByKeyword(results, keyword)
return filtered, nil
}
// extractResultsFromBytes 从字节数组中提取搜索结果(优化版本,避免字符串转换)
func (p *AshPlugin) extractResultsFromBytes(data []byte) ([]model.SearchResult, error) {
// 直接在字节数组中查找JSON数据(避免转换为字符串)
html := string(data) // 只转换一次
// 查找JSON数据
matches := jsonDataRegex.FindStringSubmatch(html)
if len(matches) < 2 {
return []model.SearchResult{}, nil // 没有找到数据,返回空结果
}View on GitHub (pinned to beaa561337)