@skrillex1224/playwright-toolkit 2.0.22 → 2.0.24

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/dist/index.js CHANGED
@@ -5,7 +5,7 @@ var __export = (target, all) => {
5
5
  };
6
6
 
7
7
  // src/apify-kit.js
8
- import { log } from "crawlee";
8
+ import { log as originalLog } from "crawlee";
9
9
 
10
10
  // src/constants.js
11
11
  var constants_exports = {};
@@ -31,7 +31,65 @@ var StatusCode = {
31
31
  var FAILED_KEY_SEPARATOR = "::<@>::";
32
32
  var PresetOfLiveViewKey = "LIVE_VIEW_SCREENSHOT";
33
33
 
34
+ // src/logger.js
35
+ import { log } from "crawlee";
36
+ var GLOBAL_PREFIX = "[Toolkit]";
37
+ function createLogger(moduleName) {
38
+ const prefix = `${GLOBAL_PREFIX}[${moduleName}]`;
39
+ return {
40
+ /**
41
+ * 方法开始日志
42
+ * @param {string} methodName - 方法名称
43
+ * @param {string} [params] - 参数摘要 (可选)
44
+ */
45
+ start(methodName, params = "") {
46
+ const paramStr = params ? ` (${params})` : "";
47
+ log.info(`${prefix} \u{1F537} ${methodName} \u5F00\u59CB${paramStr}`);
48
+ },
49
+ /**
50
+ * 方法成功日志
51
+ * @param {string} methodName - 方法名称
52
+ * @param {string} [result] - 结果摘要 (可选)
53
+ */
54
+ success(methodName, result = "") {
55
+ const resultStr = result ? ` (${result})` : "";
56
+ log.info(`${prefix} \u2705 ${methodName} \u5B8C\u6210${resultStr}`);
57
+ },
58
+ /**
59
+ * 方法失败日志
60
+ * @param {string} methodName - 方法名称
61
+ * @param {Error|string} error - 错误对象或信息
62
+ */
63
+ fail(methodName, error) {
64
+ const message = error instanceof Error ? error.message : error;
65
+ log.error(`${prefix} \u274C ${methodName} \u5931\u8D25: ${message}`);
66
+ },
67
+ /**
68
+ * 调试日志
69
+ * @param {string} message - 详情
70
+ */
71
+ debug(message) {
72
+ log.debug(`${prefix} \u{1F539} ${message}`);
73
+ },
74
+ /**
75
+ * 警告日志
76
+ * @param {string} message - 警告信息
77
+ */
78
+ warn(message) {
79
+ log.warning(`${prefix} \u26A0\uFE0F ${message}`);
80
+ },
81
+ /**
82
+ * 普通信息日志
83
+ * @param {string} message - 信息
84
+ */
85
+ info(message) {
86
+ log.info(`${prefix} ${message}`);
87
+ }
88
+ };
89
+ }
90
+
34
91
  // src/apify-kit.js
92
+ var logger = createLogger("ApifyKit");
35
93
  async function createApifyKit() {
36
94
  let apify = null;
37
95
  try {
@@ -65,13 +123,13 @@ async function createApifyKit() {
65
123
  async runStep(pendingStepName, page, actionFn, options = {}) {
66
124
  const { failActor = true } = options;
67
125
  const [failedKey, stepName] = this.unwrapStepName(pendingStepName);
68
- log.info(`\u{1F504} [\u6B63\u5728\u6267\u884C] ${stepName}...`);
126
+ logger.start(`[Step] ${stepName}`);
69
127
  try {
70
128
  const result = await actionFn();
71
- log.info(`\u2705 [\u6267\u884C\u6210\u529F] ${stepName}`);
129
+ logger.success(`[Step] ${stepName}`);
72
130
  return result;
73
131
  } catch (error) {
74
- log.error(`\u274C [\u6267\u884C\u5931\u8D25] ${stepName}: ${error.message}`);
132
+ logger.fail(`[Step] ${stepName}`, error);
75
133
  let screenshotBase64 = "\u622A\u56FE\u5931\u8D25";
76
134
  try {
77
135
  if (page) {
@@ -79,7 +137,7 @@ async function createApifyKit() {
79
137
  screenshotBase64 = `data:image/jpeg;base64,${buffer.toString("base64")}`;
80
138
  }
81
139
  } catch (snapErr) {
82
- log.warning(`\u622A\u56FE\u751F\u6210\u5931\u8D25: ${snapErr.message}`);
140
+ logger.warn(`\u622A\u56FE\u751F\u6210\u5931\u8D25: ${snapErr.message}`);
83
141
  }
84
142
  await this.pushFailed(error, {
85
143
  failedStep: stepName,
@@ -112,6 +170,7 @@ async function createApifyKit() {
112
170
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
113
171
  ...data
114
172
  });
173
+ logger.success("pushSuccess", "Data pushed");
115
174
  },
116
175
  /**
117
176
  * 推送失败数据的通用方法(私有方法,仅供runStep内部使用)
@@ -128,6 +187,7 @@ async function createApifyKit() {
128
187
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
129
188
  ...meta
130
189
  });
190
+ logger.success("pushFailed", "Error data pushed");
131
191
  }
132
192
  };
133
193
  }
@@ -143,6 +203,7 @@ var ApifyKit = {
143
203
  };
144
204
 
145
205
  // src/utils.js
206
+ var logger2 = createLogger("Utils");
146
207
  var Utils = {
147
208
  /**
148
209
  * 解析 SSE 流文本
@@ -161,12 +222,13 @@ var Utils = {
161
222
  }
162
223
  }
163
224
  }
225
+ logger2.success("parseSseStream", `parsed events: ${events.length}`);
164
226
  return events;
165
227
  }
166
228
  };
167
229
 
168
230
  // src/stealth.js
169
- import { log as log2 } from "crawlee";
231
+ var logger3 = createLogger("Stealth");
170
232
  var Stealth = {
171
233
  /**
172
234
  * 关键修复:将 Page 视口调整为与浏览器指纹 (window.screen) 一致。
@@ -185,9 +247,9 @@ var Stealth = {
185
247
  width: screen.width,
186
248
  height: screen.height
187
249
  });
188
- log2.info(`[Stealth] Viewport synced to fingerprint: ${screen.width}x${screen.height}`);
250
+ logger3.success("syncViewportWithScreen", `size=${screen.width}x${screen.height}`);
189
251
  } catch (e) {
190
- log2.warning(`[Stealth] Failed to sync viewport: ${e.message}. Fallback to 1920x1080.`);
252
+ logger3.warn(`syncViewportWithScreen Failed: ${e.message}. Fallback to 1920x1080.`);
191
253
  await page.setViewportSize({ width: 1920, height: 1080 });
192
254
  }
193
255
  },
@@ -200,6 +262,7 @@ var Stealth = {
200
262
  get: () => false
201
263
  });
202
264
  });
265
+ logger3.success("hideWebdriver");
203
266
  },
204
267
  /**
205
268
  * 通用的 Playwright 资源拦截器,用于屏蔽不必要的资源以加速加载
@@ -215,6 +278,7 @@ var Stealth = {
215
278
  }
216
279
  return route.continue();
217
280
  });
281
+ logger3.success("setupBlockingResources", `types=[${resourceTypes.join(",")}]`);
218
282
  },
219
283
  /**
220
284
  * 获取推荐的 Stealth 启动参数
@@ -276,13 +340,13 @@ var Stealth = {
276
340
  return -480;
277
341
  };
278
342
  });
279
- log2.info("[Stealth] \u65F6\u533A\u5DF2\u8BBE\u7F6E\u4E3A Asia/Shanghai (UTC+8)");
343
+ logger3.success("setChinaTimezone", "Asia/Shanghai (UTC+8)");
280
344
  }
281
345
  };
282
346
 
283
347
  // src/humanize.js
284
348
  import delay, { rangeDelay } from "delay";
285
- import { log as log3 } from "crawlee";
349
+ var logger4 = createLogger("Humanize");
286
350
  var Humanize = {
287
351
  /**
288
352
  * 随机延迟一段毫秒数 (API Wrapper for 'delay' package)
@@ -290,8 +354,10 @@ var Humanize = {
290
354
  * @param {number} max - 最大毫秒
291
355
  */
292
356
  async randomSleep(min, max) {
357
+ logger4.start("randomSleep", `min=${min}, max=${max}`);
293
358
  const ms = typeof max === "number" ? rangeDelay(min, max) : delay(min);
294
359
  await ms;
360
+ logger4.success("randomSleep", `waited=${await ms}ms`);
295
361
  },
296
362
  /**
297
363
  * 模拟人类“注视”或“阅读”行为:鼠标在页面上随机微动。
@@ -299,6 +365,7 @@ var Humanize = {
299
365
  * @param {number} durationMs - 持续时间
300
366
  */
301
367
  async simulateGaze(cursor, durationMs = 2e3) {
368
+ logger4.start("simulateGaze", `duration=${durationMs}ms`);
302
369
  const startTime = Date.now();
303
370
  while (Date.now() - startTime < durationMs) {
304
371
  const x = Math.random() * 800;
@@ -306,6 +373,7 @@ var Humanize = {
306
373
  await cursor.actions.move({ x, y });
307
374
  await rangeDelay(200, 800);
308
375
  }
376
+ logger4.success("simulateGaze");
309
377
  },
310
378
  /**
311
379
  * 人类化输入 - 带节奏变化(快-慢-停顿-偶尔加速)
@@ -320,6 +388,7 @@ var Humanize = {
320
388
  * @param {number} [options.pauseMax=800] - 停顿最大时长 (ms)
321
389
  */
322
390
  async humanType(page, selector, text, options = {}) {
391
+ logger4.start("humanType", `selector=${selector}, textLen=${text.length}`);
323
392
  const {
324
393
  minDelay = 50,
325
394
  maxDelay = 200,
@@ -327,26 +396,32 @@ var Humanize = {
327
396
  pauseMin = 300,
328
397
  pauseMax = 800
329
398
  } = options;
330
- const locator = page.locator(selector);
331
- await locator.click();
332
- await rangeDelay(100, 300);
333
- for (let i = 0; i < text.length; i++) {
334
- const char = text[i];
335
- let charDelay;
336
- if (char === " ") {
337
- charDelay = minDelay + Math.random() * 50;
338
- } else if (/[,.!?;:,。!?;:]/.test(char)) {
339
- charDelay = maxDelay + Math.random() * 100;
340
- } else {
341
- charDelay = minDelay + Math.random() * (maxDelay - minDelay);
342
- }
343
- await page.keyboard.type(char);
344
- await delay(charDelay);
345
- if (Math.random() < pauseProbability && i < text.length - 1) {
346
- const pauseTime = pauseMin + Math.random() * (pauseMax - pauseMin);
347
- log3.debug(`[Humanize] \u505C\u987F ${Math.round(pauseTime)}ms...`);
348
- await delay(pauseTime);
399
+ try {
400
+ const locator = page.locator(selector);
401
+ await locator.click();
402
+ await rangeDelay(100, 300);
403
+ for (let i = 0; i < text.length; i++) {
404
+ const char = text[i];
405
+ let charDelay;
406
+ if (char === " ") {
407
+ charDelay = minDelay + Math.random() * 50;
408
+ } else if (/[,.!?;:,。!?;:]/.test(char)) {
409
+ charDelay = maxDelay + Math.random() * 100;
410
+ } else {
411
+ charDelay = minDelay + Math.random() * (maxDelay - minDelay);
412
+ }
413
+ await page.keyboard.type(char);
414
+ await delay(charDelay);
415
+ if (Math.random() < pauseProbability && i < text.length - 1) {
416
+ const pauseTime = pauseMin + Math.random() * (pauseMax - pauseMin);
417
+ logger4.debug(`\u505C\u987F ${Math.round(pauseTime)}ms...`);
418
+ await delay(pauseTime);
419
+ }
349
420
  }
421
+ logger4.success("humanType");
422
+ } catch (error) {
423
+ logger4.fail("humanType", error);
424
+ throw error;
350
425
  }
351
426
  },
352
427
  /**
@@ -363,25 +438,30 @@ var Humanize = {
363
438
  durationMs = duration;
364
439
  }
365
440
  durationMs = Math.round(durationMs);
366
- log3.info(`[Humanize] \u5F00\u59CB\u9875\u9762\u9884\u70ED\u6D4F\u89C8 (${durationMs}ms)...`);
441
+ logger4.start("warmUpBrowsing", `duration=${durationMs}ms`);
367
442
  const startTime = Date.now();
368
443
  const viewportSize = page.viewportSize() || { width: 1920, height: 1080 };
369
- while (Date.now() - startTime < durationMs) {
370
- const action = Math.random();
371
- if (action < 0.4) {
372
- const x = 100 + Math.random() * (viewportSize.width - 200);
373
- const y = 100 + Math.random() * (viewportSize.height - 200);
374
- await cursor.actions.move({ x, y });
375
- await rangeDelay(200, 500);
376
- } else if (action < 0.7) {
377
- const scrollY = (Math.random() - 0.5) * 200;
378
- await page.mouse.wheel(0, scrollY);
379
- await rangeDelay(300, 700);
380
- } else {
381
- await rangeDelay(500, 1e3);
444
+ try {
445
+ while (Date.now() - startTime < durationMs) {
446
+ const action = Math.random();
447
+ if (action < 0.4) {
448
+ const x = 100 + Math.random() * (viewportSize.width - 200);
449
+ const y = 100 + Math.random() * (viewportSize.height - 200);
450
+ await cursor.actions.move({ x, y });
451
+ await rangeDelay(200, 500);
452
+ } else if (action < 0.7) {
453
+ const scrollY = (Math.random() - 0.5) * 200;
454
+ await page.mouse.wheel(0, scrollY);
455
+ await rangeDelay(300, 700);
456
+ } else {
457
+ await rangeDelay(500, 1e3);
458
+ }
382
459
  }
460
+ logger4.success("warmUpBrowsing");
461
+ } catch (error) {
462
+ logger4.fail("warmUpBrowsing", error);
463
+ throw error;
383
464
  }
384
- log3.info("[Humanize] \u9875\u9762\u9884\u70ED\u5B8C\u6210");
385
465
  },
386
466
  /**
387
467
  * 自然滚动 - 带惯性和减速效果
@@ -391,14 +471,21 @@ var Humanize = {
391
471
  * @param {number} [steps=5] - 分几步完成
392
472
  */
393
473
  async naturalScroll(page, direction = "down", distance = 300, steps = 5) {
474
+ logger4.start("naturalScroll", `dir=${direction}, dist=${distance}, steps=${steps}`);
394
475
  const sign = direction === "down" ? 1 : -1;
395
476
  const stepDistance = distance / steps;
396
- for (let i = 0; i < steps; i++) {
397
- const factor = 1 - i / steps * 0.5;
398
- const scrollAmount = stepDistance * factor * sign;
399
- await page.mouse.wheel(0, scrollAmount);
400
- const delayTime = 50 + i * 30;
401
- await delay(delayTime);
477
+ try {
478
+ for (let i = 0; i < steps; i++) {
479
+ const factor = 1 - i / steps * 0.5;
480
+ const scrollAmount = stepDistance * factor * sign;
481
+ await page.mouse.wheel(0, scrollAmount);
482
+ const delayTime = 50 + i * 30;
483
+ await delay(delayTime);
484
+ }
485
+ logger4.success("naturalScroll");
486
+ } catch (error) {
487
+ logger4.fail("naturalScroll", error);
488
+ throw error;
402
489
  }
403
490
  },
404
491
  /**
@@ -415,39 +502,47 @@ var Humanize = {
415
502
  * @param {boolean} [options.throwOnMissing=true] - 元素不存在时是否抛出错误
416
503
  */
417
504
  async humanClick(page, cursor, selector, options = {}) {
505
+ logger4.start("humanClick", `selector=${selector}`);
418
506
  const {
419
507
  delayBefore = 300,
420
508
  delayAfter = 800,
421
509
  throwOnMissing = true
422
510
  } = options;
423
- const element = await page.$(selector);
424
- if (!element) {
425
- if (throwOnMissing) {
426
- throw new Error(`[Humanize] humanClick: \u627E\u4E0D\u5230\u5143\u7D20 ${selector}`);
511
+ try {
512
+ const element = await page.$(selector);
513
+ if (!element) {
514
+ if (throwOnMissing) {
515
+ throw new Error(`\u627E\u4E0D\u5230\u5143\u7D20 ${selector}`);
516
+ }
517
+ logger4.warn(`humanClick: \u5143\u7D20\u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7\u70B9\u51FB ${selector}`);
518
+ return false;
427
519
  }
428
- log3.warning(`[Humanize] humanClick: \u5143\u7D20\u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7\u70B9\u51FB ${selector}`);
429
- return false;
430
- }
431
- const box = await element.boundingBox();
432
- if (!box) {
433
- if (throwOnMissing) {
434
- throw new Error(`[Humanize] humanClick: \u65E0\u6CD5\u83B7\u53D6\u5143\u7D20\u4F4D\u7F6E ${selector}`);
520
+ const box = await element.boundingBox();
521
+ if (!box) {
522
+ if (throwOnMissing) {
523
+ throw new Error(`\u65E0\u6CD5\u83B7\u53D6\u5143\u7D20\u4F4D\u7F6E ${selector}`);
524
+ }
525
+ logger4.warn(`humanClick: \u65E0\u6CD5\u83B7\u53D6\u4F4D\u7F6E\uFF0C\u8DF3\u8FC7\u70B9\u51FB ${selector}`);
526
+ return false;
435
527
  }
436
- log3.warning(`[Humanize] humanClick: \u65E0\u6CD5\u83B7\u53D6\u4F4D\u7F6E\uFF0C\u8DF3\u8FC7\u70B9\u51FB ${selector}`);
437
- return false;
528
+ const offsetX = (Math.random() - 0.5) * box.width * 0.3;
529
+ const offsetY = (Math.random() - 0.5) * box.height * 0.3;
530
+ const x = box.x + box.width / 2 + offsetX;
531
+ const y = box.y + box.height / 2 + offsetY;
532
+ await cursor.actions.move({ x, y });
533
+ await rangeDelay(delayBefore, delayAfter);
534
+ await cursor.actions.click();
535
+ logger4.success("humanClick");
536
+ return true;
537
+ } catch (error) {
538
+ logger4.fail("humanClick", error);
539
+ throw error;
438
540
  }
439
- const offsetX = (Math.random() - 0.5) * box.width * 0.3;
440
- const offsetY = (Math.random() - 0.5) * box.height * 0.3;
441
- const x = box.x + box.width / 2 + offsetX;
442
- const y = box.y + box.height / 2 + offsetY;
443
- await cursor.actions.move({ x, y });
444
- await rangeDelay(delayBefore, delayAfter);
445
- await cursor.actions.click();
446
- return true;
447
541
  }
448
542
  };
449
543
 
450
544
  // src/launch.js
545
+ var logger5 = createLogger("Launch");
451
546
  var Launch = {
452
547
  getLaunchOptions(customArgs = []) {
453
548
  return {
@@ -502,6 +597,7 @@ var Launch = {
502
597
  */
503
598
  createStealthChromium(chromium, stealthPlugin) {
504
599
  chromium.use(stealthPlugin());
600
+ logger5.success("createStealthChromium", "Stealth plugin registered");
505
601
  return chromium;
506
602
  },
507
603
  /**
@@ -514,14 +610,16 @@ var Launch = {
514
610
  * @returns {Promise<import('ghost-cursor-playwright').Cursor>}
515
611
  */
516
612
  async createGhostCursor(page, createCursor) {
517
- return await createCursor(page);
613
+ const cursor = await createCursor(page);
614
+ logger5.success("createGhostCursor", "initialized");
615
+ return cursor;
518
616
  }
519
617
  };
520
618
 
521
619
  // src/live-view.js
522
620
  import express from "express";
523
- import { log as log4 } from "crawlee";
524
621
  import { Actor } from "apify";
622
+ var logger6 = createLogger("LiveView");
525
623
  async function startLiveViewServer(liveViewKey) {
526
624
  const app = express();
527
625
  app.get("/", async (req, res) => {
@@ -546,13 +644,13 @@ async function startLiveViewServer(liveViewKey) {
546
644
  </html>
547
645
  `);
548
646
  } catch (error) {
549
- log4.error(`Live View \u670D\u52A1\u5668\u9519\u8BEF: ${error.message}`);
647
+ logger6.fail("Live View Server", error);
550
648
  res.status(500).send(`\u65E0\u6CD5\u52A0\u8F7D\u5C4F\u5E55\u622A\u56FE: ${error.message}`);
551
649
  }
552
650
  });
553
651
  const port = process.env.APIFY_CONTAINER_PORT || 4321;
554
652
  app.listen(port, () => {
555
- log4.info(`Live View \u670D\u52A1\u5668\u5DF2\u542F\u52A8\uFF0C\u76D1\u542C\u7AEF\u53E3 ${port}\u3002\u8BF7\u6253\u5F00 "Live View" \u9009\u9879\u5361\u67E5\u770B\u3002`);
653
+ logger6.success("startLiveViewServer", `\u76D1\u542C\u7AEF\u53E3 ${port}`);
556
654
  });
557
655
  }
558
656
  async function takeLiveScreenshot(liveViewKey, page, logMessage) {
@@ -560,10 +658,10 @@ async function takeLiveScreenshot(liveViewKey, page, logMessage) {
560
658
  const buffer = await page.screenshot({ type: "png" });
561
659
  await Actor.setValue(liveViewKey, buffer, { contentType: "image/png" });
562
660
  if (logMessage) {
563
- log4.info(`(\u622A\u56FE): ${logMessage}`);
661
+ logger6.info(`(\u622A\u56FE): ${logMessage}`);
564
662
  }
565
663
  } catch (e) {
566
- log4.warning(`\u65E0\u6CD5\u6355\u83B7 Live View \u5C4F\u5E55\u622A\u56FE: ${e.message}`);
664
+ logger6.warn(`\u65E0\u6CD5\u6355\u83B7 Live View \u5C4F\u5E55\u622A\u56FE: ${e.message}`);
567
665
  }
568
666
  }
569
667
  var useLiveView = (liveViewKey = PresetOfLiveViewKey) => {
@@ -581,7 +679,7 @@ var LiveView = {
581
679
  };
582
680
 
583
681
  // src/captcha-monitor.js
584
- import { log as log5 } from "crawlee";
682
+ var logger7 = createLogger("Captcha");
585
683
  function useCaptchaMonitor(page, options) {
586
684
  const { domSelector, urlPattern, onDetected } = options;
587
685
  if (!domSelector && !urlPattern) {
@@ -596,7 +694,7 @@ function useCaptchaMonitor(page, options) {
596
694
  const triggerDetected = async () => {
597
695
  if (isHandled) return;
598
696
  isHandled = true;
599
- log5.error("[CaptchaMonitor] \u{1F6D1} \u68C0\u6D4B\u5230\u9A8C\u8BC1\u7801\uFF01");
697
+ logger7.fail("Captcha Detected", "\u{1F6D1} \u68C0\u6D4B\u5230\u9A8C\u8BC1\u7801\uFF01");
600
698
  await onDetected();
601
699
  };
602
700
  if (domSelector) {
@@ -646,7 +744,7 @@ function useCaptchaMonitor(page, options) {
646
744
  }
647
745
  })();
648
746
  }, { selector: domSelector, callbackName: exposedFunctionName });
649
- log5.info(`[CaptchaMonitor] DOM \u76D1\u63A7\u5DF2\u542F\u7528: ${domSelector}`);
747
+ logger7.success("useCaptchaMonitor", `DOM \u76D1\u63A7\u5DF2\u542F\u7528: ${domSelector}`);
650
748
  }
651
749
  if (urlPattern) {
652
750
  frameHandler = async (frame) => {
@@ -658,7 +756,7 @@ function useCaptchaMonitor(page, options) {
658
756
  }
659
757
  };
660
758
  page.on("framenavigated", frameHandler);
661
- log5.info(`[CaptchaMonitor] URL \u76D1\u63A7\u5DF2\u542F\u7528: ${urlPattern}`);
759
+ logger7.success("useCaptchaMonitor", `URL \u76D1\u63A7\u5DF2\u542F\u7528: ${urlPattern}`);
662
760
  }
663
761
  }
664
762
  var Captcha = {