@skrillex1224/playwright-toolkit 2.1.192 → 2.1.194
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 +255 -255
- package/browser.d.ts +8 -8
- package/dist/browser.js +1544 -17
- package/dist/browser.js.map +4 -4
- package/dist/index.cjs +0 -15
- package/dist/index.cjs.map +2 -2
- package/dist/index.js +0 -15
- package/dist/index.js.map +2 -2
- package/dist/internals/proxy-meter.js +549 -549
- package/index.d.ts +636 -636
- package/package.json +66 -66
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;
|