{"record":{"id":"c24a863852d142df","repo":"DIYgod/RSSHub","slug":"id-c24a86","errorCode":null,"errorMessage":"无法从页面中获取到帖子ID，可能网站结构已变更","messagePattern":"无法从页面中获取到帖子ID，可能网站结构已变更","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/routes/jandan/utils.ts","lineNumber":106,"sourceCode":"                link: `${rootUrl}/new/forum/topic/${post.post_id}`,\n                category: post.reply_count > 0 ? [`${post.reply_count}条回复`] : undefined,\n            }) as DataItem\n    );\n\n    return { title, items, link: currentUrl };\n};\n\n/**\n * Handle other sections (问答, 树洞, 随手拍, 女装, 无聊图)\n */\nexport const handleCommentSection = async (rootUrl: string, category: string): Promise<{ title: string; items: DataItem[] }> => {\n    const currentUrl = `${rootUrl}/${category}`;\n\n    const { pageId, title: pageTitle } = await extractPageMeta(currentUrl);\n    const title = pageTitle || `煎蛋 - ${category}`;\n\n    if (!pageId) {\n        throw new Error('无法从页面中获取到帖子ID，可能网站结构已变更');\n    }\n\n    const apiUrl = `${rootUrl}/api/comment/post/${pageId}?order=desc&page=0`;\n    const commentsData = await ofetch(apiUrl);\n\n    if (commentsData.code !== 0) {\n        throw new Error('没有获取到内容，可能需要更新解析规则');\n    }\n\n    const items = commentsData.data.list.map((comment) => {\n        const content = comment.content.replaceAll(/img src=\"(.*?)\"/g, (match, src) => match.replace(src, () => src.replace(/^https?:\\/\\/(\\w+)\\.moyu\\.im/, 'https://$1.sinaimg.cn')));\n\n        return {\n            author: comment.author,\n            title: `${comment.author}: ${sanitizeHtml(content, { allowedTags: [], allowedAttributes: {} })}`,\n            description: content,\n            pubDate: parseDate(comment.date_gmt),\n            link: `${rootUrl}/t/${comment.id}`,","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/jandan/utils.ts#L88-L124","documentation":"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.","triggerScenarios":"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.","commonSituations":"煎蛋 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.","solutions":["Open the category URL in a browser, view-source, and confirm whether `PAGE = { id:` still exists in an inline script","If the script tag changed, update the regex in extractPageMeta (lib/routes/jandan/utils.ts:20-22) to match the new shape","If the value now lives in an external JS bundle or a JSON blob, fetch that resource instead and parse it","Clear the `jandan:pageMeta:${url}` cache key so a stale empty pageId is not reused"],"exampleFix":"// before\npageId:\n    $('script:contains(\"PAGE\")')\n        .text()\n        .match(/PAGE\\s*=\\s*\\{\\s*id\\s*:\\s*(\\d+)\\s*\\}/)?.[1] ?? '',\n// after\nconst scripts = $('script').toArray().map((el) => $(el).text()).join('\\n');\nconst pageId = scripts.match(/PAGE\\s*=\\s*\\{\\s*id\\s*:\\s*(\\d+)\\s*\\}/)?.[1]\n    ?? scripts.match(/\"pageId\"\\s*:\\s*(\\d+)/)?.[1]\n    ?? '';","handlingStrategy":"validation","validationCode":"// Validate the page actually exposes the PAGE id before delegating to the handler\nasync function pageHasPageId(url: string): Promise<boolean> {\n  const html = await ofetch(url);\n  const $ = load(html);\n  const scripts = $('script').toArray().map((el) => $(el).text()).join('\\n');\n  return /PAGE\\s*=\\s*\\{\\s*id\\s*:\\s*\\d+\\s*\\}/.test(scripts);\n}","typeGuard":"function hasPageId(meta: { pageId?: string }): meta is { pageId: string } {\n  return typeof meta.pageId === 'string' && /^\\d+$/.test(meta.pageId);\n}","tryCatchPattern":"try {\n  return await handleCommentSection(rootUrl, category);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('帖子ID')) {\n    // bust the cached pageMeta then retry once — likely a stale empty extraction\n    await cache.tryGet.delete?.(`jandan:pageMeta:${rootUrl}/${category}`);\n    throw new Error('jandan page structure changed; extraction regex needs updating');\n  }\n  throw e;\n}","preventionTips":["Cache pageMeta with a short TTL so structural changes self-heal","Pin extraction to multiple fallback patterns (PAGE global, JSON island, data attribute)","Add a content-length / content-type sanity check on the fetched page to detect anti-bot interstitials","Log a snippet of the fetched HTML when pageId extraction fails"],"tags":["scraping","html-parsing","site-restructure"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}