{"record":{"id":"2513458a68b5c38f","repo":"jackwener/OpenCLI","slug":"dianping-contexthint-did-not-render-expected-da","errorCode":null,"errorMessage":"dianping ${contextHint} did not render expected data${sample}","messagePattern":"dianping (.+?) did not render expected data(.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/dianping/utils.js","lineNumber":122,"sourceCode":"export function detectAuthOrPageFailure({ text = '', url = '' }, contextHint, { emptyPatterns = [] } = {}) {\n    const signal = `${url} ${text}`;\n    if (/verify\\.meituan\\.com|verifyimg|身份核实|请依次点击|美团安全验证|Yoda/i.test(signal)) {\n        throw new AuthRequiredError(\n            'dianping.com',\n            `dianping ${contextHint} blocked by captcha — open ${url || 'www.dianping.com'} manually in this profile and solve the captcha, then retry`,\n        );\n    }\n    if (/login\\.dianping\\.com|account\\.dianping\\.com|请先登录|未登录|请登录/.test(signal)) {\n        throw new AuthRequiredError(\n            'dianping.com',\n            `dianping ${contextHint} requires login — sign in to dianping.com in this profile, then retry`,\n        );\n    }\n    if (emptyPatterns.some((pattern) => pattern.test(signal))) {\n        throw new EmptyResultError(`dianping ${contextHint}`);\n    }\n    const sample = text ? `; sample: ${String(text).slice(0, 160)}` : '';\n    throw new CommandExecutionError(\n        `dianping ${contextHint} did not render expected data${sample}`,\n        'This usually means dianping changed its HTML, returned an unexpected error page, or the browser profile hit an unrecognized anti-bot state.',\n    );\n}\n\n/**\n * Parse \"21231\" / \"1.2万\" review-count strings into integers.\n * Returns null when the input has no parseable digits.\n */\nexport function parseReviewCount(raw) {\n    if (raw == null) return null;\n    const s = String(raw).trim();\n    if (!s) return null;\n    const wanMatch = s.match(/^([\\d.]+)\\s*万/);\n    if (wanMatch) {\n        const n = Number(wanMatch[1]);\n        return Number.isFinite(n) ? Math.round(n * 10000) : null;\n    }","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/dianping/utils.js#L104-L140","documentation":"detectAuthOrPageFailure is the dianping CLI's guard against pages that loaded but contain no recognizable data. After the page text fails the emptyPatterns check (EmptyResultError), it throws CommandExecutionError meaning the page rendered something, but not the expected content — typically a layout change, an error page, or an unrecognized anti-bot/captcha state. The message includes a 160-char sample of the page text to help diagnose what actually rendered.","triggerScenarios":"Calling a dianping CLI command when the page HTML no longer matches expected selectors/patterns: dianping ships a DOM redesign, the browser profile is flagged and served an interstitial challenge not covered by emptyPatterns, a soft error page (rate-limit, login wall) renders, or the network returned truncated content.","commonSituations":"Dianping frontend update breaks selectors; scraping from an IP flagged by anti-bot; stale/expired cookies in the persistent browser profile; running headless with a profile that gets served a verification page.","solutions":["Inspect the '; sample:' portion of the message to see what actually rendered (captcha, login wall, redesign).","Open the browser profile manually, complete any captcha/login, and retry so the profile has valid session state.","Update the CLI's selectors/emptyPatterns to match dianping's current markup if the site changed.","Retry later or from a different network/IP if the sample shows an anti-bot or rate-limit page.","Run the command non-headless (visible browser) to interactively pass challenges."],"exampleFix":"// before: blindly retrying the same command\nawait cli.run('dianping shop 12345');\n// after: check sample text / refresh profile state first\ntry {\n  await cli.run('dianping shop 12345');\n} catch (e) {\n  if (e instanceof CommandExecutionError && /sample:/.test(e.message)) {\n    await refreshBrowserProfile(); // re-login / solve captcha manually\n    await cli.run('dianping shop 12345');\n  }\n}","handlingStrategy":"try-catch","validationCode":"// check page state before re-running\nconst text = await page.evaluate('document.body.innerText');\nif (/captcha|verify|log in|access denied/i.test(text)) await resolveAntiBotManually();","typeGuard":"function looksLikeCaptcha(text) {\n  return /captcha|verify you are human|access denied|异常|验证/i.test(text);\n}","tryCatchPattern":"try {\n  await runDianpingCommand(args);\n} catch (e) {\n  if (/did not render expected data/.test(e.message)) {\n    const sample = e.message.split('sample:')[1] || '';\n    console.error('Page rendered unexpected content:', sample);\n    // refresh profile/login then retry once\n  }\n}","preventionTips":["Keep the browser profile logged in and captcha-free by using it interactively periodically.","Log the sample text from every failure to spot dianping redesigns early.","Keep selectors/emptyPatterns updated after Discord/dianping UI changes.","Add backoff between requests to avoid anti-bot flagging.","Run non-headless first when a new site layout is suspected."],"tags":["scraping","anti-bot","dom-change","browser-automation"],"backgroundTag":"page-layout-changed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}