fish2018/pansou · error
[ ] 网页搜索 HTML 解析失败
Error message
[%s] 网页搜索 HTML 解析失败: %w
What it means
The feikuai plugin falls back to scraping a web search page when its primary API call fails. goquery could not parse the HTML response body returned by the fallback search endpoint. This indicates the response was not valid/complete HTML (blocked page, truncated body, or non-HTML content type).
Solutions
- Retry the request; transient truncation often succeeds on a second attempt.
- Check the fallback search endpoint with curl to see what content is actually returned (captcha page, JSON, empty body).
- Set a realistic User-Agent/headers so the site does not serve an anti-bot page.
- Improve API reliability (credentials, rate limits) so the HTML fallback is not needed.
Example fix
// before
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
return nil, fmt.Errorf("[%s] 网页搜索 HTML 解析失败: %w", p.Name(), err)
}
// after
doc, err := goquery.NewDocumentFromReader(io.LimitReader(resp.Body, 5<<20))
if err != nil {
log.Printf("[%s] fallback HTML parse failed (status %d, content-type %s)", p.Name(), resp.StatusCode, resp.Header.Get("Content-Type"))
return nil, fmt.Errorf("[%s] 网页搜索 HTML 解析失败: %w", p.Name(), err)
} Defensive patterns
Strategy: try-catch
Validate before calling
// before calling search, check the site responds with HTML
resp, err := http.Get(fallbackURL)
if err == nil {
ct := resp.Header.Get("Content-Type")
if !strings.Contains(ct, "text/html") {
// skip fallback scrape
}
} Type guard
func isHTMLResponse(resp *http.Response) bool {
return resp != nil && strings.Contains(resp.Header.Get("Content-Type"), "text/html")
} Try / catch
results, err := plugin.Search(keyword)
if err != nil {
var parseErr *goqueryError // or use errors.Is/As on wrapped causes
log.Printf("search failed (fallback HTML unparseable): %v", err)
results = []model.SearchResult{} // degrade gracefully
} Prevention
- Set a browser-like User-Agent to avoid anti-bot pages
- Validate Content-Type is text/html before parsing
- Retry transient failures before giving up
- Keep the primary API healthy so the HTML fallback is rarely used
When it happens
Trigger: searchWeb: the API attempt failed (apiErr set), the fallback web-search HTTP response returned status 200, but goquery.NewDocumentFromReader failed to parse resp.Body's HTML.
Common situations: Site returns a captcha/challenge page with malformed HTML, CDN truncates the response, server returns JSON or binary instead of HTML on the fallback URL, network proxy corrupts the body.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/ef6c4bf2a4aa76be.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/feikuai/feikuai.go:193
req, err := http.NewRequestWithContext(ctx, http.MethodGet, searchURL, nil)
if err != nil {
return nil, fmt.Errorf("[%s] 创建网页搜索请求失败: %w", p.Name(), err)
}
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/136.0.0.0 Safari/537.36")
req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
req.Header.Set("Referer", "https://feikuai.in/")
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("[%s] API 失败且网页搜索请求失败: %v (API: %v)", p.Name(), err, apiErr)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("[%s] API 失败且网页搜索返回状态码 %d (API: %v)", p.Name(), resp.StatusCode, apiErr)
}
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
return nil, fmt.Errorf("[%s] 网页搜索 HTML 解析失败: %w", p.Name(), err)
}
results := make([]model.SearchResult, 0, 64)
seen := make(map[string]struct{})
add := func(linkURL, title, content string, datetime time.Time) {
linkURL = strings.TrimSpace(linkURL)
if linkURL == "" {
return
}
linkType := util.GetLinkType(linkURL)
if linkType == "" || linkType == "others" {
return
}
if _, ok := seen[linkURL]; ok {
return
}
seen[linkURL] = struct{}{}
if title == "" {View on GitHub (pinned to beaa561337)