DIYgod/RSSHub · error · Error

无法从页面中获取到帖子ID,可能网站结构已变更

Error message

无法从页面中获取到帖子ID,可能网站结构已变更

What it means

Thrown by handleCommentSection in the jandan route when extractPageMeta cannot find a page id. extractPageMeta scrapes the category page's inline scripts and matches `/PAGE\s*=\s*\{\s*id\s*:\s*(\d+)\s*\}/`; if no script tag containing 'PAGE' yields that pattern, pageId becomes '' and the handler refuses to continue (Chinese: 'could not get post id from page, site structure may have changed'). It is a defensive guard because every subsequent API call depends on that id.

Source

Thrown at lib/routes/jandan/utils.ts:106

                link: `${rootUrl}/new/forum/topic/${post.post_id}`,
                category: post.reply_count > 0 ? [`${post.reply_count}条回复`] : undefined,
            }) as DataItem
    );

    return { title, items, link: currentUrl };
};

/**
 * Handle other sections (问答, 树洞, 随手拍, 女装, 无聊图)
 */
export const handleCommentSection = async (rootUrl: string, category: string): Promise<{ title: string; items: DataItem[] }> => {
    const currentUrl = `${rootUrl}/${category}`;

    const { pageId, title: pageTitle } = await extractPageMeta(currentUrl);
    const title = pageTitle || `煎蛋 - ${category}`;

    if (!pageId) {
        throw new Error('无法从页面中获取到帖子ID,可能网站结构已变更');
    }

    const apiUrl = `${rootUrl}/api/comment/post/${pageId}?order=desc&page=0`;
    const commentsData = await ofetch(apiUrl);

    if (commentsData.code !== 0) {
        throw new Error('没有获取到内容,可能需要更新解析规则');
    }

    const items = commentsData.data.list.map((comment) => {
        const content = comment.content.replaceAll(/img src="(.*?)"/g, (match, src) => match.replace(src, () => src.replace(/^https?:\/\/(\w+)\.moyu\.im/, 'https://$1.sinaimg.cn')));

        return {
            author: comment.author,
            title: `${comment.author}: ${sanitizeHtml(content, { allowedTags: [], allowedAttributes: {} })}`,
            description: content,
            pubDate: parseDate(comment.date_gmt),
            link: `${rootUrl}/t/${comment.id}`,

View on GitHub (pinned to bed535e087)

Solutions

  1. Open the category URL in a browser, view-source, and confirm whether `PAGE = { id:` still exists in an inline script
  2. If the script tag changed, update the regex in extractPageMeta (lib/routes/jandan/utils.ts:20-22) to match the new shape
  3. If the value now lives in an external JS bundle or a JSON blob, fetch that resource instead and parse it
  4. Clear the `jandan:pageMeta:${url}` cache key so a stale empty pageId is not reused

Example fix

// before
pageId:
    $('script:contains("PAGE")')
        .text()
        .match(/PAGE\s*=\s*\{\s*id\s*:\s*(\d+)\s*\}/)?.[1] ?? '',
// after
const scripts = $('script').toArray().map((el) => $(el).text()).join('\n');
const pageId = scripts.match(/PAGE\s*=\s*\{\s*id\s*:\s*(\d+)\s*\}/)?.[1]
    ?? scripts.match(/"pageId"\s*:\s*(\d+)/)?.[1]
    ?? '';
Defensive patterns

Strategy: validation

Validate before calling

// Validate the page actually exposes the PAGE id before delegating to the handler
async function pageHasPageId(url: string): Promise<boolean> {
  const html = await ofetch(url);
  const $ = load(html);
  const scripts = $('script').toArray().map((el) => $(el).text()).join('\n');
  return /PAGE\s*=\s*\{\s*id\s*:\s*\d+\s*\}/.test(scripts);
}

Type guard

function hasPageId(meta: { pageId?: string }): meta is { pageId: string } {
  return typeof meta.pageId === 'string' && /^\d+$/.test(meta.pageId);
}

Try / catch

try {
  return await handleCommentSection(rootUrl, category);
} catch (e) {
  if (e instanceof Error && e.message.includes('帖子ID')) {
    // bust the cached pageMeta then retry once — likely a stale empty extraction
    await cache.tryGet.delete?.(`jandan:pageMeta:${rootUrl}/${category}`);
    throw new Error('jandan page structure changed; extraction regex needs updating');
  }
  throw e;
}

Prevention

When it happens

Trigger: GET `${rootUrl}/${category}` returns HTML, but the inline `PAGE = { id: <number> }` assignment is gone, renamed, minified to a different shape, or moved into an external bundle that cheerio does not execute. Also fires when ofetch receives an error/interstitial page (HTTP 200 with anti-bot body) instead of the real category page.

Common situations: 煎蛋 ships a frontend rebuild that changes the PAGE global name; anti-bot middleware returns a challenge page; the cached pageMeta is stale and points at a now-redirecting URL; category slug passed in does not correspond to a real page so the returned HTML is the homepage.

Related errors


AI-assisted analysis of DIYgod/RSSHub@bed535e087 (2026-08-12). Data as JSON: /api/errors/c24a863852d142df. Report an issue: GitHub.