fish2018/pansou · error

[ ] 未能在首页找到 data-key

Error message

[%s] 未能在首页找到 data-key

What it means

Returned when the homepage HTML parsed successfully but no <script> tag content matches dataKeyRegex, so the plugin cannot extract the data-key value needed to call the Ghost Content API. This indicates the site's page structure changed or the served page no longer embeds the expected data-key attribute.

Solutions

  1. Open https://jsnoteclub.com/ in a browser, view source, and locate where data-key now appears
  2. Update dataKeyRegex to match the new attribute/markup pattern
  3. If the key moved out of script tags, search the full document HTML (doc.Find("[data-key]")) instead of only script elements
  4. Check whether the Ghost Content API key is published in a JS bundle or meta tag now and adjust extraction
  5. Fallback: configure the data-key statically via plugin config instead of scraping

Example fix

// before
match := dataKeyRegex.FindStringSubmatch(htmlBuilder.String())
if len(match) < 2 {
    return "", fmt.Errorf("[%s] 未能在首页找到 data-key", p.Name())
}
// after
match := dataKeyRegex.FindStringSubmatch(htmlBuilder.String())
if len(match) < 2 {
    // fallback: look for data-key attribute anywhere in the document
    if sel := doc.Find("[data-key]"); sel.Length() > 0 {
        if key, ok := sel.Attr("data-key"); ok && key != "" {
            return key, nil
        }
    }
    return "", fmt.Errorf("[%s] 未能在首页找到 data-key", p.Name())
}
Defensive patterns

Strategy: fallback

Validate before calling

// check the site still embeds the key before running the pipeline
resp, err := http.Get("https://jsnoteclub.com/")
if err != nil { /* network problem, different error */ }
body, _ := io.ReadAll(resp.Body)
if !strings.Contains(string(body), "data-key") && !dataKeyRegex.MatchString(string(body)) {
    // upstream layout changed: alert/maintain config-supplied key instead
}

Try / catch

key, err := plugin.fetchDataKey(client)
if err != nil {
    if strings.Contains(err.Error(), "未能") {
        // selector rot: fall back to a configured static key
        return useConfiguredDataKey()
    }
    return err
}

Prevention

When it happens

Trigger: The regex finds no match (len(match) < 2) after scanning the outer HTML of every <script> element in the homepage document — i.e. no script tag contains the data-key pattern the regex expects.

Common situations: jsnoteclub.com redesigned its frontend and the data-key is no longer in a <script> tag; the Ghost site switched themes so the key moved to a different attribute; the plugin scrapes a JS-rendered shell where content loads via XHR; site now serves a maintenance or consent page instead of the app HTML.

Related errors


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

Appendix: source

Thrown at plugin/jsnoteclub/jsnoteclub.go:258

	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("[%s] 首页返回状态码: %d", p.Name(), resp.StatusCode)
	}

	doc, err := goquery.NewDocumentFromReader(resp.Body)
	if err != nil {
		return "", fmt.Errorf("[%s] 解析首页失败: %w", p.Name(), err)
	}

	var htmlBuilder strings.Builder
	doc.Find("script").Each(func(_ int, s *goquery.Selection) {
		if html, err := goquery.OuterHtml(s); err == nil {
			htmlBuilder.WriteString(html)
		}
	})

	match := dataKeyRegex.FindStringSubmatch(htmlBuilder.String())
	if len(match) < 2 {
		return "", fmt.Errorf("[%s] 未能在首页找到 data-key", p.Name())
	}

	return match[1], nil
}

func (p *JsNoteClubPlugin) fetchPosts(client *http.Client, dataKey string) ([]ghostPost, error) {
	params := url.Values{}
	params.Set("key", dataKey)
	params.Set("limit", "10000")
	params.Set("fields", "id,slug,title,excerpt,url,updated_at,visibility")
	params.Set("order", "updated_at DESC")

	reqURL := fmt.Sprintf("https://jsnoteclub.com/ghost/api/content/posts/?%s", params.Encode())

	ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
	defer cancel()

	req, err := http.NewRequestWithContext(ctx, http.MethodGet, reqURL, nil)

View on GitHub (pinned to beaa561337)