DIYgod/RSSHub · error · Error

Unable to find s-data in page

Error message

Unable to find s-data in page

What it means

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).

Source

Thrown at lib/routes/baidu/top.tsx:63

    description: `| 热搜榜   | 小说榜 | 电影榜 | 电视剧榜 | 汽车榜 | 游戏榜 |
| -------- | ------ | ------ | -------- | ------ | ------ |
| realtime | novel  | movie  | teleplay | car    | game   |`,
};

async function handler(ctx) {
    const { board = 'realtime' } = ctx.req.param();
    const link = `https://top.baidu.com/board?tab=${board}`;
    const { data: response } = await got(link);

    const $ = load(response);

    const sDataMatch = $('#sanRoot')
        .contents()
        .toArray()
        .find((e): e is Comment => e.nodeType === 8)
        ?.data.match(/s-data:(.*)/);
    if (!sDataMatch) {
        throw new Error('Unable to find s-data in page');
    }
    const { data } = JSON.parse(sDataMatch[1]);

    const items = data.cards[0].content.map((item) => ({
        title: item.word,
        description: renderDescription(item),
        link: item.rawUrl,
    }));

    return {
        title: `${data.curBoardName} - 百度热搜`,
        description: $('meta[name="description"]').attr('content'),
        link,
        item: items,
    };
}

View on GitHub (pinned to bed535e087)

Solutions

  1. Fetch https://top.baidu.com/board?tab=realtime in a browser, view source, and locate where the JSON now lives.
  2. Update the extraction at lib/routes/baidu/top.tsx:55 to read the new container (e.g. a script tag with JSON.parse).
  3. If the data now comes from an XHR, call that API directly instead of scraping the HTML.

Example fix

// before
const sDataMatch = $('#sanRoot').contents().toArray().find(...)?.data.match(/s-data:(.*)/);
// after
const raw = $('#__NEXT_DATA__').text();
const { data } = JSON.parse(raw);
Defensive patterns

Strategy: validation

Validate before calling

function pageHasSData($: ReturnType<typeof load>): boolean {
  const comment = $('#sanRoot').contents().toArray().find((e: any) => e.nodeType === 8);
  return !!comment && /s-data:/.test(comment.data ?? '');
}

Type guard

const hasSDataComment = ($: ReturnType<typeof load>): boolean =>
  /s-data:/.test($('#sanRoot').contents().toArray().map((e: any) => e.data ?? '').join('\n'));

Try / catch

try {
  return await handler(ctx);
} catch (e) {
  if (e instanceof Error && /Unable to find s-data/.test(e.message)) {
    ctx.throw(502, 'Baidu top page markup changed - update extractor');
  }
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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