@cloud411716/fancy-webnovel 0.1.76 → 0.1.78

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/infra.js +26 -21
  2. package/package.json +1 -1
  3. package/plugins/fancy-bootstrap/index.js +4 -2
  4. package/plugins/fancy-scan/index.js +1 -2
  5. package/plugins/fancy-scan/scripts/run-scan.js +22 -44
  6. package/plugins/fancy-scan/scripts/scraper-registry.md +3 -5
  7. package/plugins/fancy-scan/scripts/scrapers/cdp-utils.cjs +7 -5
  8. package/plugins/fancy-scan/scripts/scrapers/fanqie-rank-scraper.cjs +282 -253
  9. package/plugins/fancy-scan/scripts/scrapers/jjwxc-rank-scraper.cjs +193 -253
  10. package/plugins/fancy-scan/scripts/scrapers/node_modules/.package-lock.json +20 -0
  11. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/LICENSE +202 -0
  12. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/NOTICE +5 -0
  13. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/README.md +3 -0
  14. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/ThirdPartyNotices.txt +13 -0
  15. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/bin/install_media_pack.ps1 +5 -0
  16. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/bin/install_webkit_wsl.ps1 +33 -0
  17. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/bin/reinstall_chrome_beta_linux.sh +42 -0
  18. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/bin/reinstall_chrome_beta_mac.sh +13 -0
  19. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/bin/reinstall_chrome_beta_win.ps1 +24 -0
  20. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/bin/reinstall_chrome_stable_linux.sh +42 -0
  21. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/bin/reinstall_chrome_stable_mac.sh +12 -0
  22. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/bin/reinstall_chrome_stable_win.ps1 +24 -0
  23. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/bin/reinstall_msedge_beta_linux.sh +48 -0
  24. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/bin/reinstall_msedge_beta_mac.sh +11 -0
  25. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/bin/reinstall_msedge_beta_win.ps1 +23 -0
  26. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/bin/reinstall_msedge_dev_linux.sh +48 -0
  27. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/bin/reinstall_msedge_dev_mac.sh +11 -0
  28. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/bin/reinstall_msedge_dev_win.ps1 +23 -0
  29. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/bin/reinstall_msedge_stable_linux.sh +48 -0
  30. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/bin/reinstall_msedge_stable_mac.sh +11 -0
  31. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/bin/reinstall_msedge_stable_win.ps1 +24 -0
  32. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/browsers.json +75 -0
  33. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/cli.js +21 -0
  34. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/index.d.ts +17 -0
  35. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/index.js +17 -0
  36. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/index.mjs +28 -0
  37. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/bootstrap.js +88 -0
  38. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/coreBundle.js +74830 -0
  39. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/entry/cliDaemon.js +5 -0
  40. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/entry/dashboardApp.js +3 -0
  41. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/entry/mcp.js +10 -0
  42. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/entry/oopBrowserDownload.js +3 -0
  43. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/package.js +50 -0
  44. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/server/chromium/appIcon.png +0 -0
  45. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/server/electron/loader.js +118 -0
  46. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/serverRegistry.js +7347 -0
  47. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/serverRegistry.js.LICENSE +354 -0
  48. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/tools/cli-client/channelSessions.js +141 -0
  49. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/tools/cli-client/cli.js +6 -0
  50. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/tools/cli-client/help.json +708 -0
  51. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/tools/cli-client/minimist.js +128 -0
  52. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/tools/cli-client/output.js +343 -0
  53. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/tools/cli-client/program.js +404 -0
  54. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/tools/cli-client/registry.js +176 -0
  55. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/tools/cli-client/session.js +258 -0
  56. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/tools/dashboard/appIcon.png +0 -0
  57. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/tools/skills/playwright-cli/SKILL.md +420 -0
  58. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/element-attributes.md +23 -0
  59. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/playwright-tests.md +39 -0
  60. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/request-mocking.md +87 -0
  61. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/running-code.md +241 -0
  62. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/session-management.md +225 -0
  63. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/storage-state.md +275 -0
  64. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/test-generation.md +433 -0
  65. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/tracing.md +139 -0
  66. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/tools/skills/playwright-cli/references/video-recording.md +143 -0
  67. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/SKILL.md +143 -0
  68. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/gallery-spec.md +144 -0
  69. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/migration.md +85 -0
  70. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/react.md +67 -0
  71. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/vue.md +75 -0
  72. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/tools/skills/playwright-trace/SKILL.md +171 -0
  73. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/tools/utils/extension.js +101 -0
  74. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/tools/utils/socketConnection.js +108 -0
  75. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/utilsBundle.js +90764 -0
  76. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/utilsBundle.js.LICENSE +2179 -0
  77. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/dashboard/assets/codicon-DCmgc-ay.ttf +0 -0
  78. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/dashboard/assets/firefox-1bWoP6pv.svg +1 -0
  79. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/dashboard/assets/firefox-beta-k3eOH_eK.svg +1 -0
  80. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/dashboard/assets/firefox-nightly-Cp5nfeDT.svg +1 -0
  81. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/dashboard/assets/index-CyWAfh-p.js +11 -0
  82. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/dashboard/assets/index-DhC616m4.css +1 -0
  83. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/dashboard/assets/safari-na3_-uQk.svg +1 -0
  84. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/dashboard/index.html +29 -0
  85. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/dashboard/playwright-logo.svg +24 -0
  86. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/htmlReport/index.html +16 -0
  87. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/htmlReport/report.css +2 -0
  88. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/htmlReport/report.js +32 -0
  89. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule--QdMvsKi.css +1 -0
  90. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule-CwYYHbcZ.js +32 -0
  91. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/recorder/assets/codicon-DCmgc-ay.ttf +0 -0
  92. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/recorder/assets/index-DYjdXIbE.js +129 -0
  93. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/recorder/assets/index-l_lX622x.css +1 -0
  94. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/recorder/index.html +29 -0
  95. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/recorder/playwright-logo.svg +9 -0
  96. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/traceViewer/assets/codeMirrorModule-rXmQmLUY.js +32 -0
  97. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-B-dXF5JN.js +181 -0
  98. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/traceViewer/assets/urlMatch-L3liM589.js +1 -0
  99. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/traceViewer/assets/xtermModule-BuZfJS5v.js +7 -0
  100. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/traceViewer/codeMirrorModule.-QdMvsKi.css +1 -0
  101. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/traceViewer/codicon.DCmgc-ay.ttf +0 -0
  102. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/traceViewer/defaultSettingsView.BLFoOugd.css +1 -0
  103. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/traceViewer/index.B_TqY17P.css +1 -0
  104. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/traceViewer/index.KZ4wOW1K.js +1 -0
  105. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/traceViewer/index.html +44 -0
  106. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/traceViewer/manifest.webmanifest +16 -0
  107. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/traceViewer/playwright-logo.svg +9 -0
  108. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/traceViewer/snapshot.B_Jk1wbt.js +1 -0
  109. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/traceViewer/snapshot.html +10 -0
  110. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/traceViewer/sw.bundle.js +4 -0
  111. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/traceViewer/uiMode.C7UW1sC9.css +1 -0
  112. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/traceViewer/uiMode.Dzuouizj.js +5 -0
  113. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/traceViewer/uiMode.html +18 -0
  114. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/vite/traceViewer/xtermModule.kHJ-D0s7.css +1 -0
  115. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/webp_codec.LICENSE +173 -0
  116. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/webp_codec.wasm +0 -0
  117. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/lib/xdg-open +1267 -0
  118. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/package.json +34 -0
  119. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/types/protocol.d.ts +24842 -0
  120. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/types/structs.d.ts +52 -0
  121. package/plugins/fancy-scan/scripts/scrapers/node_modules/playwright-core/types/types.d.ts +26188 -0
  122. package/plugins/fancy-scan/scripts/scrapers/package-lock.json +27 -0
  123. package/plugins/fancy-scan/scripts/scrapers/package.json +9 -0
  124. package/plugins/fancy-scan/scripts/scrapers/qimao-rank-scraper.cjs +200 -297
  125. package/plugins/fancy-scan/scripts/scrapers/zhihu-rank-scraper.cjs +80 -80
@@ -2,8 +2,8 @@
2
2
  /**
3
3
  * 知乎短篇/长篇采集脚本
4
4
  *
5
+ * 使用 playwright-core 自己管理浏览器。
5
6
  * 入口:https://www.zhihu.com/fiore/h5/vip-web
6
- *
7
7
  * 短篇(4 个榜单):推荐榜 / 热搜榜 / 热度榜 / 口碑榜
8
8
  * 长篇(1 个榜单):长篇榜
9
9
  *
@@ -11,30 +11,27 @@
11
11
  * node zhihu-rank-scraper.js --length short # 短篇 4 个榜单
12
12
  * node zhihu-rank-scraper.js --length long # 长篇 1 个榜单
13
13
  * node zhihu-rank-scraper.js --length short --boards rec,hot # 指定短篇子榜
14
+ * node zhihu-rank-scraper.js --login-wait 60 # 等待手动登录
14
15
  *
15
- * 前置:
16
- * node {SKILL_DIR}/browser-cdp/scripts/setup-cdp-chrome.js 9222
17
- * 知乎需要登录态才能看完整榜单,请先用 Chrome 手动登录 zhihu.com
18
- *
19
- * 输出:扫榜结果/知乎{长篇|短篇}{榜单名}_{YYYYMMDD}.md
16
+ * 注意:知乎需要登录态才能看完整榜单。
20
17
  */
21
18
 
22
19
  const fs = require("fs");
23
20
  const path = require("path");
24
- const { ab, evalJSONBase64, getArg, localDateStamp, runCli } = require("./cdp-utils.cjs");
21
+ const { chromium } = require("playwright-core");
22
+ const { getArg, localDateStamp, runCli } = require("./cdp-utils.cjs");
25
23
 
26
- const PORT = 9222;
27
24
  const OUTDIR = getArg(process.argv, "--outdir") || "扫榜结果";
28
25
  const ENTRY_URL = "https://www.zhihu.com/fiore/h5/vip-web";
29
-
30
26
  const LENGTH = getArg(process.argv, "--length") || "short";
27
+ const LOGIN_WAIT = parseInt(getArg(process.argv, "--login-wait") || "0", 10);
31
28
 
32
29
  // 短篇 4 个榜单
33
30
  const SHORT_BOARDS = [
34
- { id: "rec", label: "推荐榜", tabText: "推荐榜" },
35
- { id: "hot", label: "热搜榜", tabText: "热搜榜" },
36
- { id: "trending", label: "热度榜", tabText: "热度榜" },
37
- { id: "reputation", label: "口碑榜", tabText: "口碑榜" },
31
+ { id: "rec", label: "推荐榜", tabText: "推荐榜" },
32
+ { id: "hot", label: "热搜榜", tabText: "热搜榜" },
33
+ { id: "trending", label: "热度榜", tabText: "热度榜" },
34
+ { id: "reputation",label: "口碑榜", tabText: "口碑榜" },
38
35
  ];
39
36
 
40
37
  // 长篇 1 个榜单
@@ -44,83 +41,88 @@ const LONG_BOARDS = [
44
41
 
45
42
  const BOARDS = LENGTH === "long" ? LONG_BOARDS : SHORT_BOARDS;
46
43
 
47
- // 自定义子榜(可选)
48
44
  const customBoards = getArg(process.argv, "--boards");
49
45
  const targetBoards = customBoards
50
- ? BOARDS.filter((b) => customBoards.split(",").map((s) => s.trim()).includes(b.id))
46
+ ? BOARDS.filter(b => customBoards.split(",").map(s => s.trim()).includes(b.id))
51
47
  : BOARDS;
52
48
 
53
- /**
54
- * 在页面里点指定 tab,等待内容刷新,再提取故事列表
55
- */
56
- async function scrapeBoard(port, board) {
49
+ // ---------------------------------------------------------------------------
50
+ // 工具函数
51
+ // ---------------------------------------------------------------------------
52
+
53
+ function sleep(ms) {
54
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
55
+ }
56
+
57
+ // ---------------------------------------------------------------------------
58
+ // 采集
59
+ // ---------------------------------------------------------------------------
60
+
61
+ async function scrapeBoard(board) {
57
62
  console.log(` [${board.label}] 正在抓取...`);
58
63
 
59
- // 导航到入口页(每次都重新进入首页,避免 SPA 状态污染)
60
- const navResult = ab(port, "navigate", ENTRY_URL);
61
- if (!navResult.ok) {
62
- console.error(` [${board.label}] 导航失败: ${navResult.error || "unknown"}`);
63
- return null;
64
- }
64
+ const browser = await chromium.launch({ headless: true, args: ["--no-sandbox", "--disable-dev-shm-usage"] });
65
+ const context = await browser.newContext();
66
+ const page = await context.newPage();
67
+
68
+ try {
69
+ await page.goto(ENTRY_URL, { waitUntil: "networkidle" });
70
+
71
+ if (LOGIN_WAIT > 0) {
72
+ console.log(` ⏳ 等待 ${LOGIN_WAIT}s 供用户登录...`);
73
+ await page.waitForTimeout(LOGIN_WAIT * 1000);
74
+ }
75
+
76
+ const host = page.url();
77
+ if (host.indexOf("zhihu") === -1) {
78
+ console.error(` ✗ 当前页面非知乎(url=${host}),可能被重定向,已跳过。`);
79
+ return null;
80
+ }
65
81
 
66
- // 等待页面加载
67
- await new Promise((r) => setTimeout(r, 3000));
68
-
69
- // 点击对应 tab
70
- // 用包含 tabText 的元素点击(兼容多种 class 命名)
71
- const clickJs = `
72
- (function(){
73
- const target = ${JSON.stringify(board.tabText)};
74
- const candidates = Array.from(document.querySelectorAll('a, button, div, span, li'))
75
- .filter((el) => {
76
- const t = (el.innerText || '').trim();
82
+ // 点击 tab
83
+ const clicked = await page.evaluate((target) => {
84
+ const candidates = Array.from(document.querySelectorAll("a, button, div, span, li"))
85
+ .filter(el => {
86
+ const t = (el.innerText || "").trim();
77
87
  return t === target || t.startsWith(target);
78
88
  });
79
- if (candidates.length === 0) return JSON.stringify({ok:false, reason:'tab_not_found', target:target});
89
+ if (!candidates.length) return false;
80
90
  candidates[0].click();
81
- return JSON.stringify({ok:true, clicked:candidates[0].tagName});
82
- })()
83
- `;
84
- const clickResult = evalJSONBase64(port, clickJs);
85
- if (!clickResult || !clickResult.ok) {
86
- console.error(` [${board.label}] 点击 tab 失败: ${JSON.stringify(clickResult)}`);
87
- return null;
88
- }
91
+ return true;
92
+ }, board.tabText);
93
+
94
+ if (!clicked) {
95
+ console.error(` [${board.label}] 点击 tab 失败`);
96
+ return null;
97
+ }
89
98
 
90
- // 等待内容加载
91
- await new Promise((r) => setTimeout(r, 2500));
99
+ await page.waitForTimeout(2500);
92
100
 
93
- // 提取故事列表
94
- // 兼容多种可能的选择器(知乎没有公开 class 文档,按常见模式)
95
- const storiesJs = `
96
- (function(){
101
+ // 提取故事列表
102
+ const stories = await page.evaluate(() => {
97
103
  const items = [];
98
104
  const seen = new Set();
99
-
100
- // 策略 1:找所有含故事标题的 anchor
101
105
  const anchors = document.querySelectorAll('a[href*="/p/"], a[href*="/question/"], a[href*="/xen/"]');
102
106
  anchors.forEach((a, i) => {
103
- const href = a.href || '';
107
+ const href = a.href || "";
104
108
  if (!href || seen.has(href)) return;
105
- const text = (a.innerText || '').trim();
109
+ const text = (a.innerText || "").trim();
106
110
  if (!text || text.length < 4 || text.length > 200) return;
107
- // 跳过导航/按钮文字
108
111
  if (/^(首页|发现|等你来答|登录|注册|更多|查看全部)$/.test(text)) return;
109
112
  seen.add(href);
110
- items.push({rank: i + 1, title: text.split('\\n')[0].trim(), url: href});
113
+ items.push({ rank: i + 1, title: text.split("\n")[0].trim(), url: href });
111
114
  });
115
+ return items.slice(0, 100);
116
+ });
112
117
 
113
- return JSON.stringify(items.slice(0, 100));
114
- })()
115
- `;
116
-
117
- const stories = evalJSONBase64(port, storiesJs);
118
- if (!stories || stories.length === 0) {
119
- console.warn(` [${board.label}] 未提取到故事`);
120
- return [];
118
+ if (!stories || stories.length === 0) {
119
+ console.warn(` [${board.label}] 未提取到故事`);
120
+ return [];
121
+ }
122
+ return stories;
123
+ } finally {
124
+ await browser.close();
121
125
  }
122
-
123
- return stories;
124
126
  }
125
127
 
126
128
  function buildBoardMarkdown(board, stories, date) {
@@ -129,36 +131,35 @@ function buildBoardMarkdown(board, stories, date) {
129
131
  "",
130
132
  `> 数据来源:${ENTRY_URL} → ${board.label}`,
131
133
  `> 篇幅:${LENGTH === "long" ? "长篇" : "短篇"}`,
132
- `> 知乎需要登录态才能看完整榜单,请确认 Chrome 已登录`,
134
+ "> 知乎需要登录态才能看完整榜单,请确认已登录",
133
135
  "",
134
136
  `## ${board.label}内容`,
135
137
  "",
136
138
  ];
137
-
138
- if (stories.length === 0) {
139
- lines.push("(未提取到数据,可能页面结构变化或需要登录)", "");
139
+ if (!stories.length) {
140
+ lines.push("(未提取到数据,可能页面结构变化或需要登录)", "", "---", "");
140
141
  } else {
141
- stories.forEach((s) => {
142
+ stories.forEach(s => {
142
143
  lines.push(`### ${s.rank}. ${s.title || "(无标题)"}`);
143
144
  if (s.url) lines.push(`- 链接:${s.url}`);
144
145
  lines.push("", "---", "");
145
146
  });
146
147
  }
147
-
148
148
  return lines.join("\n");
149
149
  }
150
150
 
151
- function main() {
151
+ // ---------------------------------------------------------------------------
152
+ // 入口
153
+ // ---------------------------------------------------------------------------
154
+
155
+ async function main() {
152
156
  const date = localDateStamp();
153
157
  fs.mkdirSync(OUTDIR, { recursive: true });
154
158
 
155
159
  let written = 0;
156
160
  for (const board of targetBoards) {
157
- const stories = scrapeBoard(PORT, board);
158
- if (stories === null) {
159
- // 采集失败(导航失败 / tab 找不到),跳过这个榜单继续下一个
160
- continue;
161
- }
161
+ const stories = await scrapeBoard(board);
162
+ if (stories === null) continue;
162
163
 
163
164
  const md = buildBoardMarkdown(board, stories, date);
164
165
  const filename = `知乎${LENGTH === "long" ? "长篇" : "短篇"}${board.label}_${date}.md`;
@@ -167,7 +168,6 @@ function main() {
167
168
  console.log(` ✓ ${filename} (${stories.length} 条)`);
168
169
  written++;
169
170
  }
170
-
171
171
  return written;
172
172
  }
173
173