@skrillex1224/playwright-toolkit 2.1.190 → 2.1.192

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.
package/README.md CHANGED
@@ -1,255 +1,255 @@
1
- # Playwright Toolkit
2
-
3
- > 面向 Apify/Crawlee Actor 开发者的实用工具库,提供反检测、拟人化操作、实时截图等功能。
4
-
5
- ## 📦 安装
6
-
7
- ```bash
8
- npm install @skrillex1224/playwright-toolkit
9
-
10
- # 反检测所需的依赖
11
- npm install playwright ghost-cursor-playwright delay
12
- ```
13
-
14
- ## 🚀 快速开始
15
-
16
- ```javascript
17
- import { Actor } from 'apify';
18
- import { PlaywrightCrawler } from 'crawlee';
19
- import { chromium } from 'playwright';
20
-
21
- import { usePlaywrightToolKit } from '@skrillex1224/playwright-toolkit';
22
-
23
- await Actor.init();
24
-
25
- // 初始化工具箱
26
- const { ApifyKit: KitHook, Launch, AntiCheat, Humanize, Captcha, LiveView, Constants } = usePlaywrightToolKit();
27
-
28
- // ⚠️ ApifyKit 需要异步初始化
29
- const ApifyKit = await KitHook.useApifyKit();
30
-
31
- // LiveView
32
- const { startLiveViewServer, takeLiveScreenshot } = LiveView.useLiveView();
33
-
34
- const crawlerOptions = Launch.getPlaywrightCrawlerOptions({
35
- runInHeadfulMode: false,
36
- isRunningOnApify: Actor.isAtHome(),
37
- launcher: chromium,
38
- });
39
-
40
- const crawler = new PlaywrightCrawler({
41
- ...crawlerOptions,
42
- preNavigationHooks: [
43
- ...crawlerOptions.preNavigationHooks,
44
- async ({ page }) => {
45
- // 验证码监控
46
- Captcha.useCaptchaMonitor(page, {
47
- domSelector: '#captcha_container',
48
- onDetected: async () => { /* 处理验证码 */ }
49
- });
50
- }
51
- ],
52
- requestHandler: async ({ page }) => {
53
- // 初始化 Cursor
54
- await Humanize.initializeCursor(page);
55
-
56
- // 页面预热 (模拟人类浏览)
57
- await Humanize.warmUpBrowsing(page, 3000);
58
-
59
- // 执行步骤 (失败时自动截图并调用 Actor.fail)
60
- await ApifyKit.runStep('输入搜索', page, async () => {
61
- await Humanize.humanType(page, 'input', '搜索内容');
62
- await Humanize.humanClick(page, '#submit-btn');
63
- });
64
-
65
- // 推送成功数据
66
- await ApifyKit.pushSuccess({ result: 'data' });
67
- }
68
- });
69
-
70
- await startLiveViewServer();
71
- await crawler.run(['https://example.com']);
72
- await Actor.exit();
73
- ```
74
-
75
- ---
76
-
77
- ## 🛡️ 反检测功能
78
-
79
- ### 架构
80
-
81
- | 层次 | 问题 | 解决方案 |
82
- | ---------- | ----------------------- | --------------------------------------- |
83
- | **指纹层** | UA/屏幕/语言/时区一致性 | Crawlee `useFingerprints` + `AntiCheat` |
84
- | **行为层** | 机械输入/点击/滚动 | `ghost-cursor-playwright` + Humanize |
85
- | **页面层** | 验证码/风控检测 | Captcha 监控器 |
86
-
87
- ### API 一览
88
-
89
- | 模块 | 方法 | 说明 |
90
- | ----------- | ------------------------------------------------ | ------------------------------------------------------------------------------------ |
91
- | `Launch` | `getPlaywrightCrawlerOptions(options)` | 一次性返回 PlaywrightCrawler 所需公共配置(超时/指纹/代理/导航 hook) |
92
- | `AntiCheat` | `applyPage(page, options?)` | 应用时区/语言/权限/视口 |
93
- | `AntiCheat` | `applyContext(context, options?)` | 仅应用 Context 设置 |
94
- | `AntiCheat` | `syncViewportWithScreen(page)` | 同步视口与屏幕 |
95
- | `AntiCheat` | `getTlsFingerprintOptions(userAgent?)` | got-scraping TLS 指纹 |
96
- | `Humanize` | `initializeCursor(page)` | 初始化 Cursor (必须先调用) |
97
- | `Humanize` | `jitterMs(base, jitterPercent?)` | 生成带抖动的毫秒数 (同步,返回 number) |
98
- | `Humanize` | `humanType(page, selector, text, options?)` | 人类化输入 (baseDelay=180ms ±40%) |
99
- | `Humanize` | `humanClick(page, selector, options?)` | 人类化点击 (reactionDelay=250ms ±40%) |
100
- | `Humanize` | `warmUpBrowsing(page, baseDuration?)` | 页面预热 (3500ms ±40%) |
101
- | `Humanize` | `simulateGaze(page, baseDurationMs?)` | 模拟注视 (2500ms ±40%) |
102
- | `Humanize` | `randomSleep(baseMs, jitterPercent?)` | 随机延迟 (±30% 抖动) |
103
- | `Captcha` | `useCaptchaMonitor(page, options)` | 验证码监控 |
104
-
105
- ---
106
-
107
- ## 📦 模块详解
108
-
109
- ### ApifyKit
110
-
111
- **⚠️ 需要异步初始化**
112
-
113
- ```javascript
114
- const { ApifyKit: KitHook } = usePlaywrightToolKit();
115
- const ApifyKit = await KitHook.useApifyKit();
116
-
117
- // 执行步骤 (失败时自动截图 + 推送 Dataset + 调用 Actor.fail)
118
- await ApifyKit.runStep('步骤名', page, async () => {
119
- // 你的逻辑
120
- });
121
-
122
- // 宽松版 (失败时只抛出异常,不调用 Actor.fail)
123
- await ApifyKit.runStepLoose('步骤名', page, async () => {
124
- // 你的逻辑
125
- });
126
-
127
- // 推送成功数据 (data 字段会被包装)
128
- await ApifyKit.pushSuccess({ key: 'value' });
129
- // 输出: { code: 0, status: 'SUCCESS', timestamp: '...', data: { key: 'value' } }
130
- ```
131
-
132
- #### CrawlerError
133
-
134
- 自定义错误类,可携带 `code` 和 `context`,在 `pushFailed` 时自动解析:
135
-
136
- ```javascript
137
- const { Errors, Constants } = usePlaywrightToolKit();
138
- const { CrawlerError } = Errors;
139
- const { ErrorKeygen } = Constants;
140
-
141
- // 简单用法 (只有 message)
142
- throw new CrawlerError('未捕获 Feed 接口响应');
143
-
144
- // 完整用法 (带 code 和 context)
145
- throw new CrawlerError({
146
- message: '登录失败',
147
- code: ErrorKeygen.NotLogin, // 会作为 pushFailed 的 code 字段
148
- context: { url: currentUrl, userId: '123' }
149
- });
150
-
151
- // 从普通 Error 转换
152
- throw CrawlerError.from(originalError, {
153
- code: ErrorKeygen.Chaptcha,
154
- context: { step: '验证码检测' }
155
- });
156
-
157
- // pushFailed 输出:
158
- // { code: 30000001, status: 'FAILED', error: {...}, context: {...}, meta: {...}, ... }
159
- ```
160
-
161
- ### LiveView
162
-
163
- ```javascript
164
- const { LiveView } = usePlaywrightToolKit();
165
- const { startLiveViewServer, takeLiveScreenshot } = LiveView.useLiveView();
166
-
167
- await startLiveViewServer();
168
- await takeLiveScreenshot(page, '当前状态');
169
- ```
170
-
171
- ### Captcha
172
-
173
- ```javascript
174
- const { Captcha } = usePlaywrightToolKit();
175
-
176
- // DOM 监控模式
177
- Captcha.useCaptchaMonitor(page, {
178
- domSelector: '#captcha_container',
179
- onDetected: async () => { await Actor.fail('检测到验证码'); }
180
- });
181
-
182
- // URL 监控模式
183
- Captcha.useCaptchaMonitor(page, {
184
- urlPattern: '/captcha',
185
- onDetected: async () => { await Actor.fail('检测到验证码'); }
186
- });
187
- ```
188
-
189
- ### Constants
190
-
191
- ```javascript
192
- const { Constants } = usePlaywrightToolKit();
193
- const { ErrorKeygen, Status, StatusCode } = Constants;
194
-
195
- // ErrorKeygen: { NotLogin: 30000001, Chaptcha: 30000002 }
196
- // Status: { Success: 'SUCCESS', Failed: 'FAILED' }
197
- // StatusCode: { Success: 0, Failed: -1 }
198
- ```
199
-
200
- ### Utils
201
-
202
- ```javascript
203
- const { Utils } = usePlaywrightToolKit();
204
-
205
- // 解析 SSE 流文本
206
- const events = Utils.parseSseStream(sseText);
207
-
208
- // 解析 Cookie 字符串
209
- const cookies = Utils.parseCookies('key=value; key2=value2', '.example.com');
210
- await page.context().addCookies(cookies);
211
-
212
- // 全页面滚动截图 (自动检测所有滚动元素,强制展开后截图)
213
- const base64Image = await Share.captureScreen(page);
214
-
215
- // 移动端宽度截图(moblie 拼写保持兼容)
216
- const mobileImage = await Share.captureScreen(page, { moblie: true });
217
-
218
- // 仅恢复宽度(restore 支持 true / false / 'width-only' / 'height-only')
219
- const image2 = await Share.captureScreen(page, { restore: 'width-only' });
220
- // 返回 base64 编码的 PNG 图片
221
- ```
222
-
223
- ---
224
-
225
- ## 🧾 新增日志模板
226
-
227
- 日志模板统一维护在 `playwright-toolkit/src/logger.js` 的 `LOG_DEFINITIONS`。
228
-
229
- 1) 在 `LOG_DEFINITIONS` 新增一项,至少包含:
230
- - `key`: 全局唯一标识(用于诊断匹配)
231
- - `method`: 暴露给 `Logger.useTemplate()` 的方法名
232
- - `label/group/step/status/level/attention`
233
- - 可选 `buildDetails` / `throttleMs` / `throttleKey`
234
-
235
- 2) 在 `playwright-toolkit/index.d.ts` 的 `LoggerTemplate` 补充同名方法签名。
236
-
237
- 3) 使用示例:
238
-
239
- ```javascript
240
- import { usePlaywrightToolKit } from '@skrillex1224/playwright-toolkit';
241
-
242
- const { Logger } = usePlaywrightToolKit();
243
- const templateLogger = Logger.useTemplate();
244
- templateLogger.domChunk(120, 'preview text', true);
245
- ```
246
-
247
- 注意:
248
- - 模板会自动添加 `[#log:<key>]` 标签用于日志诊断。
249
- - 新增模板后需要重新构建/发布 toolkit。
250
-
251
- ---
252
-
253
- ## 📄 License
254
-
255
- ISC
1
+ # Playwright Toolkit
2
+
3
+ > 面向 Apify/Crawlee Actor 开发者的实用工具库,提供反检测、拟人化操作、实时截图等功能。
4
+
5
+ ## 📦 安装
6
+
7
+ ```bash
8
+ npm install @skrillex1224/playwright-toolkit
9
+
10
+ # 反检测所需的依赖
11
+ npm install playwright ghost-cursor-playwright delay
12
+ ```
13
+
14
+ ## 🚀 快速开始
15
+
16
+ ```javascript
17
+ import { Actor } from 'apify';
18
+ import { PlaywrightCrawler } from 'crawlee';
19
+ import { chromium } from 'playwright';
20
+
21
+ import { usePlaywrightToolKit } from '@skrillex1224/playwright-toolkit';
22
+
23
+ await Actor.init();
24
+
25
+ // 初始化工具箱
26
+ const { ApifyKit: KitHook, Launch, AntiCheat, Humanize, Captcha, LiveView, Constants } = usePlaywrightToolKit();
27
+
28
+ // ⚠️ ApifyKit 需要异步初始化
29
+ const ApifyKit = await KitHook.useApifyKit();
30
+
31
+ // LiveView
32
+ const { startLiveViewServer, takeLiveScreenshot } = LiveView.useLiveView();
33
+
34
+ const crawlerOptions = Launch.getPlaywrightCrawlerOptions({
35
+ runInHeadfulMode: false,
36
+ isRunningOnApify: Actor.isAtHome(),
37
+ launcher: chromium,
38
+ });
39
+
40
+ const crawler = new PlaywrightCrawler({
41
+ ...crawlerOptions,
42
+ preNavigationHooks: [
43
+ ...crawlerOptions.preNavigationHooks,
44
+ async ({ page }) => {
45
+ // 验证码监控
46
+ Captcha.useCaptchaMonitor(page, {
47
+ domSelector: '#captcha_container',
48
+ onDetected: async () => { /* 处理验证码 */ }
49
+ });
50
+ }
51
+ ],
52
+ requestHandler: async ({ page }) => {
53
+ // 初始化 Cursor
54
+ await Humanize.initializeCursor(page);
55
+
56
+ // 页面预热 (模拟人类浏览)
57
+ await Humanize.warmUpBrowsing(page, 3000);
58
+
59
+ // 执行步骤 (失败时自动截图并调用 Actor.fail)
60
+ await ApifyKit.runStep('输入搜索', page, async () => {
61
+ await Humanize.humanType(page, 'input', '搜索内容');
62
+ await Humanize.humanClick(page, '#submit-btn');
63
+ });
64
+
65
+ // 推送成功数据
66
+ await ApifyKit.pushSuccess({ result: 'data' });
67
+ }
68
+ });
69
+
70
+ await startLiveViewServer();
71
+ await crawler.run(['https://example.com']);
72
+ await Actor.exit();
73
+ ```
74
+
75
+ ---
76
+
77
+ ## 🛡️ 反检测功能
78
+
79
+ ### 架构
80
+
81
+ | 层次 | 问题 | 解决方案 |
82
+ | ---------- | ----------------------- | --------------------------------------- |
83
+ | **指纹层** | UA/屏幕/语言/时区一致性 | Crawlee `useFingerprints` + `AntiCheat` |
84
+ | **行为层** | 机械输入/点击/滚动 | `ghost-cursor-playwright` + Humanize |
85
+ | **页面层** | 验证码/风控检测 | Captcha 监控器 |
86
+
87
+ ### API 一览
88
+
89
+ | 模块 | 方法 | 说明 |
90
+ | ----------- | ------------------------------------------------ | ------------------------------------------------------------------------------------ |
91
+ | `Launch` | `getPlaywrightCrawlerOptions(options)` | 一次性返回 PlaywrightCrawler 所需公共配置(超时/指纹/代理/导航 hook) |
92
+ | `AntiCheat` | `applyPage(page, options?)` | 应用时区/语言/权限/视口 |
93
+ | `AntiCheat` | `applyContext(context, options?)` | 仅应用 Context 设置 |
94
+ | `AntiCheat` | `syncViewportWithScreen(page)` | 同步视口与屏幕 |
95
+ | `AntiCheat` | `getTlsFingerprintOptions(userAgent?)` | got-scraping TLS 指纹 |
96
+ | `Humanize` | `initializeCursor(page)` | 初始化 Cursor (必须先调用) |
97
+ | `Humanize` | `jitterMs(base, jitterPercent?)` | 生成带抖动的毫秒数 (同步,返回 number) |
98
+ | `Humanize` | `humanType(page, selector, text, options?)` | 人类化输入 (baseDelay=180ms ±40%) |
99
+ | `Humanize` | `humanClick(page, selector, options?)` | 人类化点击 (reactionDelay=250ms ±40%) |
100
+ | `Humanize` | `warmUpBrowsing(page, baseDuration?)` | 页面预热 (3500ms ±40%) |
101
+ | `Humanize` | `simulateGaze(page, baseDurationMs?)` | 模拟注视 (2500ms ±40%) |
102
+ | `Humanize` | `randomSleep(baseMs, jitterPercent?)` | 随机延迟 (±30% 抖动) |
103
+ | `Captcha` | `useCaptchaMonitor(page, options)` | 验证码监控 |
104
+
105
+ ---
106
+
107
+ ## 📦 模块详解
108
+
109
+ ### ApifyKit
110
+
111
+ **⚠️ 需要异步初始化**
112
+
113
+ ```javascript
114
+ const { ApifyKit: KitHook } = usePlaywrightToolKit();
115
+ const ApifyKit = await KitHook.useApifyKit();
116
+
117
+ // 执行步骤 (失败时自动截图 + 推送 Dataset + 调用 Actor.fail)
118
+ await ApifyKit.runStep('步骤名', page, async () => {
119
+ // 你的逻辑
120
+ });
121
+
122
+ // 宽松版 (失败时只抛出异常,不调用 Actor.fail)
123
+ await ApifyKit.runStepLoose('步骤名', page, async () => {
124
+ // 你的逻辑
125
+ });
126
+
127
+ // 推送成功数据 (data 字段会被包装)
128
+ await ApifyKit.pushSuccess({ key: 'value' });
129
+ // 输出: { code: 0, status: 'SUCCESS', timestamp: '...', data: { key: 'value' } }
130
+ ```
131
+
132
+ #### CrawlerError
133
+
134
+ 自定义错误类,可携带 `code` 和 `context`,在 `pushFailed` 时自动解析:
135
+
136
+ ```javascript
137
+ const { Errors, Constants } = usePlaywrightToolKit();
138
+ const { CrawlerError } = Errors;
139
+ const { ErrorKeygen } = Constants;
140
+
141
+ // 简单用法 (只有 message)
142
+ throw new CrawlerError('未捕获 Feed 接口响应');
143
+
144
+ // 完整用法 (带 code 和 context)
145
+ throw new CrawlerError({
146
+ message: '登录失败',
147
+ code: ErrorKeygen.NotLogin, // 会作为 pushFailed 的 code 字段
148
+ context: { url: currentUrl, userId: '123' }
149
+ });
150
+
151
+ // 从普通 Error 转换
152
+ throw CrawlerError.from(originalError, {
153
+ code: ErrorKeygen.Chaptcha,
154
+ context: { step: '验证码检测' }
155
+ });
156
+
157
+ // pushFailed 输出:
158
+ // { code: 30000001, status: 'FAILED', error: {...}, context: {...}, meta: {...}, ... }
159
+ ```
160
+
161
+ ### LiveView
162
+
163
+ ```javascript
164
+ const { LiveView } = usePlaywrightToolKit();
165
+ const { startLiveViewServer, takeLiveScreenshot } = LiveView.useLiveView();
166
+
167
+ await startLiveViewServer();
168
+ await takeLiveScreenshot(page, '当前状态');
169
+ ```
170
+
171
+ ### Captcha
172
+
173
+ ```javascript
174
+ const { Captcha } = usePlaywrightToolKit();
175
+
176
+ // DOM 监控模式
177
+ Captcha.useCaptchaMonitor(page, {
178
+ domSelector: '#captcha_container',
179
+ onDetected: async () => { await Actor.fail('检测到验证码'); }
180
+ });
181
+
182
+ // URL 监控模式
183
+ Captcha.useCaptchaMonitor(page, {
184
+ urlPattern: '/captcha',
185
+ onDetected: async () => { await Actor.fail('检测到验证码'); }
186
+ });
187
+ ```
188
+
189
+ ### Constants
190
+
191
+ ```javascript
192
+ const { Constants } = usePlaywrightToolKit();
193
+ const { ErrorKeygen, Status, StatusCode } = Constants;
194
+
195
+ // ErrorKeygen: { NotLogin: 30000001, Chaptcha: 30000002 }
196
+ // Status: { Success: 'SUCCESS', Failed: 'FAILED' }
197
+ // StatusCode: { Success: 0, Failed: -1 }
198
+ ```
199
+
200
+ ### Utils
201
+
202
+ ```javascript
203
+ const { Utils } = usePlaywrightToolKit();
204
+
205
+ // 解析 SSE 流文本
206
+ const events = Utils.parseSseStream(sseText);
207
+
208
+ // 解析 Cookie 字符串
209
+ const cookies = Utils.parseCookies('key=value; key2=value2', '.example.com');
210
+ await page.context().addCookies(cookies);
211
+
212
+ // 全页面滚动截图 (自动检测所有滚动元素,强制展开后截图)
213
+ const base64Image = await Share.captureScreen(page);
214
+
215
+ // 移动端宽度截图(moblie 拼写保持兼容)
216
+ const mobileImage = await Share.captureScreen(page, { moblie: true });
217
+
218
+ // 仅恢复宽度(restore 支持 true / false / 'width-only' / 'height-only')
219
+ const image2 = await Share.captureScreen(page, { restore: 'width-only' });
220
+ // 返回 base64 编码的 PNG 图片
221
+ ```
222
+
223
+ ---
224
+
225
+ ## 🧾 新增日志模板
226
+
227
+ 日志模板统一维护在 `playwright-toolkit/src/logger.js` 的 `LOG_DEFINITIONS`。
228
+
229
+ 1) 在 `LOG_DEFINITIONS` 新增一项,至少包含:
230
+ - `key`: 全局唯一标识(用于诊断匹配)
231
+ - `method`: 暴露给 `Logger.useTemplate()` 的方法名
232
+ - `label/group/step/status/level/attention`
233
+ - 可选 `buildDetails` / `throttleMs` / `throttleKey`
234
+
235
+ 2) 在 `playwright-toolkit/index.d.ts` 的 `LoggerTemplate` 补充同名方法签名。
236
+
237
+ 3) 使用示例:
238
+
239
+ ```javascript
240
+ import { usePlaywrightToolKit } from '@skrillex1224/playwright-toolkit';
241
+
242
+ const { Logger } = usePlaywrightToolKit();
243
+ const templateLogger = Logger.useTemplate();
244
+ templateLogger.domChunk(120, 'preview text', true);
245
+ ```
246
+
247
+ 注意:
248
+ - 模板会自动添加 `[#log:<key>]` 标签用于日志诊断。
249
+ - 新增模板后需要重新构建/发布 toolkit。
250
+
251
+ ---
252
+
253
+ ## 📄 License
254
+
255
+ ISC
package/browser.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import type { BrowserPlaywrightToolKit } from './index';
2
-
3
- export * from './index';
4
-
5
- /**
6
- * Browser-specific version of the toolkit hook.
7
- */
8
- export declare function usePlaywrightToolKit(): BrowserPlaywrightToolKit;
1
+ import type { BrowserPlaywrightToolKit } from './index';
2
+
3
+ export * from './index';
4
+
5
+ /**
6
+ * Browser-specific version of the toolkit hook.
7
+ */
8
+ export declare function usePlaywrightToolKit(): BrowserPlaywrightToolKit;
package/dist/browser.js CHANGED
@@ -1234,6 +1234,21 @@ var ActorInfo = {
1234
1234
  "share_id"
1235
1235
  ]
1236
1236
  }
1237
+ }),
1238
+ nano: createActorInfo({
1239
+ key: "nano",
1240
+ name: "\u7EB3\u7C73",
1241
+ domain: "www.n.cn",
1242
+ path: "/",
1243
+ share: {
1244
+ mode: "response",
1245
+ prefix: "https://www.n.cn/share/mcp?id=",
1246
+ xurl: [
1247
+ "/api/share/gen",
1248
+ "data",
1249
+ "id"
1250
+ ]
1251
+ }
1237
1252
  })
1238
1253
  };
1239
1254