{"record":{"id":"575eae520350a9f8","repo":"DIYgod/RSSHub","slug":"unable-to-find-s-data-in-page","errorCode":null,"errorMessage":"Unable to find s-data in page","messagePattern":"Unable to find s-data in page","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/routes/baidu/top.tsx","lineNumber":63,"sourceCode":"    description: `| 热搜榜   | 小说榜 | 电影榜 | 电视剧榜 | 汽车榜 | 游戏榜 |\n| -------- | ------ | ------ | -------- | ------ | ------ |\n| realtime | novel  | movie  | teleplay | car    | game   |`,\n};\n\nasync function handler(ctx) {\n    const { board = 'realtime' } = ctx.req.param();\n    const link = `https://top.baidu.com/board?tab=${board}`;\n    const { data: response } = await got(link);\n\n    const $ = load(response);\n\n    const sDataMatch = $('#sanRoot')\n        .contents()\n        .toArray()\n        .find((e): e is Comment => e.nodeType === 8)\n        ?.data.match(/s-data:(.*)/);\n    if (!sDataMatch) {\n        throw new Error('Unable to find s-data in page');\n    }\n    const { data } = JSON.parse(sDataMatch[1]);\n\n    const items = data.cards[0].content.map((item) => ({\n        title: item.word,\n        description: renderDescription(item),\n        link: item.rawUrl,\n    }));\n\n    return {\n        title: `${data.curBoardName} - 百度热搜`,\n        description: $('meta[name=\"description\"]').attr('content'),\n        link,\n        item: items,\n    };\n}\n","sourceCodeStart":45,"sourceCodeEnd":80,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/baidu/top.tsx#L45-L80","documentation":"The Baidu top route parses the hot-search board by finding an HTML comment inside #sanRoot whose data matches /s-data:(.*)/. If no such comment exists it throws 'Unable to find s-data in page', signalling that Baidu changed how it embeds the JSON payload (the comment-based SSR data injection).","triggerScenarios":"The fetched top.baidu.com/board HTML contains no DOM comment under #sanRoot with 's-data:' prefix - Baidu moved the JSON to a script tag, an API call, or renamed the prefix.","commonSituations":"Baidu ships a frontend rewrite that embeds initial data via <script id=\"__NEXT_DATA__\"> or a fetch call instead of the s-data comment; A/B exposure returning a different template; an anti-bot page returned instead of the real board.","solutions":["Fetch https://top.baidu.com/board?tab=realtime in a browser, view source, and locate where the JSON now lives.","Update the extraction at lib/routes/baidu/top.tsx:55 to read the new container (e.g. a script tag with JSON.parse).","If the data now comes from an XHR, call that API directly instead of scraping the HTML."],"exampleFix":"// before\nconst sDataMatch = $('#sanRoot').contents().toArray().find(...)?.data.match(/s-data:(.*)/);\n// after\nconst raw = $('#__NEXT_DATA__').text();\nconst { data } = JSON.parse(raw);","handlingStrategy":"validation","validationCode":"function pageHasSData($: ReturnType<typeof load>): boolean {\n  const comment = $('#sanRoot').contents().toArray().find((e: any) => e.nodeType === 8);\n  return !!comment && /s-data:/.test(comment.data ?? '');\n}","typeGuard":"const hasSDataComment = ($: ReturnType<typeof load>): boolean =>\n  /s-data:/.test($('#sanRoot').contents().toArray().map((e: any) => e.data ?? '').join('\\n'));","tryCatchPattern":"try {\n  return await handler(ctx);\n} catch (e) {\n  if (e instanceof Error && /Unable to find s-data/.test(e.message)) {\n    ctx.throw(502, 'Baidu top page markup changed - update extractor');\n  }\n  throw e;\n}","preventionTips":["Pin a fixture of the #sanRoot HTML in CI so a markup change fails tests before production.","Monitor this error as a leading signal of a Baidu frontend deploy.","Prefer Baidu's JSON XHR endpoint over scraping if one is available."],"tags":["baidu-top","scraping","parsing","markup-change","rss-route"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}