fish2018/pansou · error

站点触发 Cloudflare 浏览器验证

Error message

站点触发 Cloudflare 浏览器验证

What it means

This plugin-level heuristic detects Cloudflare's interstitial challenge page: if no search results were extracted and the page <title> is exactly 'Just a moment...', the site is serving a browser-verification challenge that server-side HTTP clients cannot pass, so a dedicated descriptive error is returned.

Solutions

  1. Treat the site as migrated and stop server-side scraping (see the plugin's own detection at doRequestWithRetry via cf-mitigated header)
  2. Use a headless browser (chromedp/rod) to pass the challenge
  3. Route requests through a residential proxy or retry later
  4. Check the cf-mitigated: challenge header proactively and surface a user-facing message
Defensive patterns

Strategy: fallback

Validate before calling

if doc.Find("title").First().Text() == "Just a moment..." {
    // challenge detected before extracting results
    return errChallenge
}

Try / catch

results, err := plugin.Search(ctx, kw)
if err != nil && strings.Contains(err.Error(), "Cloudflare") {
    return useHeadlessBrowserFallback(ctx, kw)
}

Prevention

When it happens

Trigger: Cloudflare bot management serves the JS challenge page with status 200; extractSearchResults finds zero results and the title matches 'Just a moment...'.

Common situations: Target site recently enabled Cloudflare under-attack mode or bot fight mode; datacenter IP or non-browser User-Agent flagged; scraping from a cloud provider IP range.

Related errors


AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07). Data as JSON: /api/errors/24c648a6723afcb6. Report an issue: GitHub.

Appendix: source

Thrown at plugin/xdpan/xdpan.go:130

	resp, err := p.doRequestWithRetry(req, client)
	if err != nil {
		return nil, fmt.Errorf("GET请求失败: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("请求返回状态码: %d", resp.StatusCode)
	}

	// 解析HTML
	doc, err := goquery.NewDocumentFromReader(io.LimitReader(resp.Body, maxPageSize))
	if err != nil {
		return nil, fmt.Errorf("解析HTML失败: %w", err)
	}

	results := p.extractSearchResults(doc)
	if len(results) == 0 && doc.Find("title").First().Text() == "Just a moment..." {
		return nil, fmt.Errorf("站点触发 Cloudflare 浏览器验证")
	}
	return results, nil
}

// extractSearchResults 从搜索页面提取结果
func (p *XdpanPlugin) extractSearchResults(doc *goquery.Document) []model.SearchResult {
	var results []model.SearchResult

	// 查找所有包含详情页链接的van-row元素
	doc.Find("van-row").Each(func(i int, s *goquery.Selection) {
		// 检查是否包含详情页链接
		detailLink := s.Find("a[href^='/s/']")
		if detailLink.Length() == 0 {
			return
		}

		result := p.parseSearchResult(s)
		if result.Title != "" {

View on GitHub (pinned to beaa561337)