{"record":{"id":"e9de94930d3126f1","repo":"jackwener/OpenCLI","slug":"no-threads-found-on-bbs-hupu-com-page-structure","errorCode":null,"errorMessage":"No threads found on bbs.hupu.com — page structure may have changed","messagePattern":"No threads found on bbs\\.hupu\\.com — page structure may have changed","errorType":"exception","errorClass":"EmptyResultError","httpStatus":null,"severity":"error","filePath":"clis/hupu/hot.js","lineNumber":138,"sourceCode":"})()\n`;\n}\n\nasync function getHupuHot(page, args) {\n    const limit = normalizeHotLimit(args.limit);\n    await page.goto(`${HUPU_HOST}/`, { waitUntil: 'load', settleMs: 1000 });\n    let rows;\n    try {\n        rows = await page.evaluate(buildHotScript(limit));\n    } catch (error) {\n        const message = error instanceof Error ? error.message : String(error);\n        throw new CommandExecutionError(\n            `Failed to read hupu hot threads: ${message}`,\n            'bbs.hupu.com may be unreachable or its markup may have changed',\n        );\n    }\n    if (!Array.isArray(rows) || rows.length === 0) {\n        throw new EmptyResultError(\n            'hupu/hot',\n            'No threads found on bbs.hupu.com — page structure may have changed',\n        );\n    }\n    return rows;\n}\n\nexport const hotCommand = cli({\n    site: 'hupu',\n    name: 'hot',\n    access: 'read',\n    description: '虎扑首页热门帖子（含 lights / replies / forum / is_hot 列）',\n    domain: 'bbs.hupu.com',\n    strategy: Strategy.PUBLIC,\n    browser: true,\n    args: [\n        { name: 'limit', type: 'int', default: HOT_LIMIT_DEFAULT, help: `Number of threads (1-${HOT_LIMIT_MAX})` },\n    ],","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/hupu/hot.js#L120-L156","documentation":"After a successful in-page extraction, getHupuHot validates that the result is a non-empty array of thread rows. An empty or non-array result means the script ran but matched nothing, which the library interprets as a changed page structure and raises EmptyResultError for the 'hupu/hot' command.","triggerScenarios":"buildHotScript's evaluate returns [] or a non-array — the hot-list DOM no longer matches the selectors, the homepage rendered a personalized/empty variant, or the region-served page omits the expected list.","commonSituations":"Hupu shipped a redesign of bbs.hupu.com; the scraper runs from a region where the hot list differs; logged-out vs logged-in layouts differ from what the script assumes; content served behind lazy-load so rows are not yet in the DOM at evaluate time.","solutions":["Increase the settle/wait time before evaluate so lazy-loaded rows exist in the DOM","Open bbs.hupu.com in a browser and compare the current markup with buildHotScript's selectors; update them if changed","Run with a logged-in/normal browser profile if the anonymous homepage variant is empty","Check whether Hupu moved the hot list to an API endpoint and switch the extraction to that source"],"exampleFix":"// before\nrows = await page.evaluate(buildHotScript(limit));\n// after\nawait page.waitForSelector('.hot-thread-item, [data-hot-list]', { timeout: 10000 }).catch(() => {});\nrows = await page.evaluate(buildHotScript(limit));","handlingStrategy":"fallback","validationCode":"await page.goto('https://bbs.hupu.com/');\nconst hasList = await page.$('.hot-thread-item, [data-hot-list]') !== null;\nif (!hasList) console.warn('Hot list selectors may be stale');","typeGuard":"function nonEmptyRows(v) { return Array.isArray(v) && v.length > 0; }","tryCatchPattern":"try {\n  rows = await getHupuHot(page, limit);\n} catch (e) {\n  if (e instanceof EmptyResultError) rows = await getHotFromFallbackSource();\n  else throw e;\n}","preventionTips":["Wait for list DOM nodes before evaluating the extraction script","Keep selectors in buildHotScript in sync with live markup","Test after Hupu deploys frontend changes","Provide an alternate hot-list source for batch pipelines"],"tags":["scraping","empty-result","hupu","markup-change"],"backgroundTag":"empty-scrape-result","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}