@skrillex1224/playwright-toolkit 2.1.57 → 2.1.59

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
@@ -82,24 +82,23 @@ await Actor.exit();
82
82
 
83
83
  ### API 一览
84
84
 
85
- | 模块 | 方法 | 说明 |
86
- | ----------- | ---------------------------------------------------- | -------------------------------------- |
87
- | `Launch` | `getAdvancedLaunchOptions()` | 增强版启动参数 |
88
- | `Launch` | `getLaunchOptions()` | 基础启动参数 |
89
- | `Launch` | `getFingerprintGeneratorOptions()` | 指纹生成器选项 |
90
- | `AntiCheat` | `applyPage(page, options?)` | 应用时区/语言/权限/视口 |
91
- | `AntiCheat` | `applyContext(context, options?)` | 仅应用 Context 设置 |
92
- | `AntiCheat` | `syncViewportWithScreen(page)` | 同步视口与屏幕 |
93
- | `AntiCheat` | `getTlsFingerprintOptions(userAgent?)` | got-scraping TLS 指纹 |
94
- | `Humanize` | `initializeCursor(page)` | 初始化 Cursor (必须先调用) |
95
- | `Humanize` | `jitterMs(base, jitterPercent?)` | 生成带抖动的毫秒数 (同步,返回 number) |
96
- | `Humanize` | `humanType(page, selector, text, options?)` | 人类化输入 (baseDelay=180ms ±40%) |
97
- | `Humanize` | `humanClick(page, selector, options?)` | 人类化点击 (reactionDelay=250ms ±40%) |
98
- | `Humanize` | `warmUpBrowsing(page, baseDuration?)` | 页面预热 (3500ms ±40%) |
99
- | `Humanize` | `naturalScroll(page, direction?, distance?, steps?)` | 自然滚动 (带惯性+抖动) |
100
- | `Humanize` | `simulateGaze(page, baseDurationMs?)` | 模拟注视 (2500ms ±40%) |
101
- | `Humanize` | `randomSleep(baseMs, jitterPercent?)` | 随机延迟 (±30% 抖动) |
102
- | `Captcha` | `useCaptchaMonitor(page, options)` | 验证码监控 |
85
+ | 模块 | 方法 | 说明 |
86
+ | ----------- | ------------------------------------------- | -------------------------------------- |
87
+ | `Launch` | `getAdvancedLaunchOptions()` | 增强版启动参数 |
88
+ | `Launch` | `getLaunchOptions()` | 基础启动参数 |
89
+ | `Launch` | `getFingerprintGeneratorOptions()` | 指纹生成器选项 |
90
+ | `AntiCheat` | `applyPage(page, options?)` | 应用时区/语言/权限/视口 |
91
+ | `AntiCheat` | `applyContext(context, options?)` | 仅应用 Context 设置 |
92
+ | `AntiCheat` | `syncViewportWithScreen(page)` | 同步视口与屏幕 |
93
+ | `AntiCheat` | `getTlsFingerprintOptions(userAgent?)` | got-scraping TLS 指纹 |
94
+ | `Humanize` | `initializeCursor(page)` | 初始化 Cursor (必须先调用) |
95
+ | `Humanize` | `jitterMs(base, jitterPercent?)` | 生成带抖动的毫秒数 (同步,返回 number) |
96
+ | `Humanize` | `humanType(page, selector, text, options?)` | 人类化输入 (baseDelay=180ms ±40%) |
97
+ | `Humanize` | `humanClick(page, selector, options?)` | 人类化点击 (reactionDelay=250ms ±40%) |
98
+ | `Humanize` | `warmUpBrowsing(page, baseDuration?)` | 页面预热 (3500ms ±40%) |
99
+ | `Humanize` | `simulateGaze(page, baseDurationMs?)` | 模拟注视 (2500ms ±40%) |
100
+ | `Humanize` | `randomSleep(baseMs, jitterPercent?)` | 随机延迟30% 抖动) |
101
+ | `Captcha` | `useCaptchaMonitor(page, options)` | 验证码监控 |
103
102
 
104
103
  ---
105
104
 
package/dist/index.cjs CHANGED
@@ -609,6 +609,17 @@ var Humanize = {
609
609
  throw error;
610
610
  }
611
611
  },
612
+ /**
613
+ * 渐进式滚动到元素可见(仅处理 Y 轴滚动)
614
+ * 返回 restore 方法,用于将滚动容器恢复到原位置
615
+ *
616
+ * @param {import('playwright').Page} page
617
+ * @param {string|import('playwright').ElementHandle} target - CSS 选择器或元素句柄
618
+ * @param {Object} [options]
619
+ * @param {number} [options.maxSteps=25] - 最大滚动步数
620
+ * @param {number} [options.minStep=80] - 单次滚动最小步长
621
+ * @param {number} [options.maxStep=220] - 单次滚动最大步长
622
+ */
612
623
  /**
613
624
  * 渐进式滚动到元素可见(仅处理 Y 轴滚动)
614
625
  * 返回 restore 方法,用于将滚动容器恢复到原位置
@@ -623,7 +634,7 @@ var Humanize = {
623
634
  async humanScroll(page, target, options = {}) {
624
635
  const { maxSteps = 30, minStep = 150, maxStep = 400 } = options;
625
636
  const targetDesc = typeof target === "string" ? target : "ElementHandle";
626
- logger5.debug(`humanScroll | \u76EE\u6807=${targetDesc}`);
637
+ logger5.info(`humanScroll | \u5F00\u59CB\u6EDA\u52A8\u76EE\u6807: ${targetDesc}`);
627
638
  let element;
628
639
  if (typeof target === "string") {
629
640
  element = await page.$(target);
@@ -636,23 +647,27 @@ var Humanize = {
636
647
  }
637
648
  const cursor = $GetCursor(page);
638
649
  let didScroll = false;
650
+ let lastRect = null;
651
+ let samePositionCount = 0;
639
652
  const checkVisibility = async () => {
640
653
  return await element.evaluate((el) => {
641
654
  const rect = el.getBoundingClientRect();
642
655
  if (!rect || rect.width === 0 || rect.height === 0) {
643
656
  return { code: "ZERO_DIMENSIONS", reason: "\u5C3A\u5BF8\u4E3A\u96F6" };
644
657
  }
645
- const cx = rect.left + rect.width / 2;
646
- const cy = rect.top + rect.height / 2;
647
658
  const viewH = window.innerHeight;
648
659
  const viewW = window.innerWidth;
660
+ const resultBase = { rect, viewH, viewW };
661
+ const cx = rect.left + rect.width / 2;
662
+ const cy = rect.top + rect.height / 2;
649
663
  if (cy < 0 || cy > viewH || cx < 0 || cx > viewW) {
650
664
  const direction = cy < 0 ? "up" : cy > viewH ? "down" : "unknown";
651
- return { code: "OUT_OF_VIEWPORT", reason: "\u4E0D\u5728\u89C6\u53E3\u5185", direction, cy, viewH };
665
+ return { ...resultBase, code: "OUT_OF_VIEWPORT", reason: "\u4E0D\u5728\u89C6\u53E3\u5185", direction, cy };
652
666
  }
653
667
  const pointElement = document.elementFromPoint(cx, cy);
654
668
  if (pointElement && !el.contains(pointElement) && !pointElement.contains(el)) {
655
669
  return {
670
+ ...resultBase,
656
671
  code: "OBSTRUCTED",
657
672
  reason: "\u88AB\u906E\u6321",
658
673
  obstruction: {
@@ -660,24 +675,39 @@ var Humanize = {
660
675
  id: pointElement.id,
661
676
  className: pointElement.className
662
677
  },
663
- cy,
664
- // Return Center Y for smart direction calculation
665
- viewH
678
+ cy
666
679
  };
667
680
  }
668
- return { code: "VISIBLE" };
681
+ return { ...resultBase, code: "VISIBLE" };
669
682
  });
670
683
  };
671
684
  try {
672
685
  for (let i = 0; i < maxSteps; i++) {
673
686
  const status = await checkVisibility();
674
687
  if (status.code === "VISIBLE") {
675
- logger5.debug("humanScroll | \u5143\u7D20\u53EF\u89C1\u4E14\u65E0\u906E\u6321");
688
+ logger5.info("humanScroll | \u5143\u7D20\u5DF2\u53EF\u89C1\u4E14\u65E0\u906E\u6321");
676
689
  return { element, didScroll };
677
690
  }
678
- logger5.debug(`humanScroll | \u6B65\u9AA4 ${i + 1}/${maxSteps}: ${status.reason} ${status.direction ? `(${status.direction})` : ""}`);
691
+ const directionStr = status.direction ? `(${status.direction})` : "";
692
+ logger5.info(`humanScroll | \u6B65\u9AA4 ${i + 1}/${maxSteps}: ${status.reason} ${directionStr}`);
693
+ const currentRect = status.rect;
694
+ if (i > 0 && lastRect && currentRect) {
695
+ const dy = Math.abs(currentRect.top - lastRect.top);
696
+ const dx = Math.abs(currentRect.left - lastRect.left);
697
+ if (dy < 2 && dx < 2) {
698
+ samePositionCount++;
699
+ logger5.info(`humanScroll | \u26A0\uFE0F \u8B66\u544A: \u6EDA\u52A8\u540E\u4F4D\u7F6E\u672A\u53D8 (${samePositionCount}/3). dy=${dy}`);
700
+ if (samePositionCount >= 3) {
701
+ logger5.warn("humanScroll | \u26A0\uFE0F \u68C0\u6D4B\u5230\u65E0\u6548\u6EDA\u52A8 (\u5143\u7D20\u53EF\u80FD\u662F Fixed \u6216\u4F4D\u4E8E\u4E0D\u53EF\u6EDA\u52A8\u7684\u5BB9\u5668)\uFF0C\u5F3A\u5236\u505C\u6B62\uFF0C\u5C1D\u8BD5\u76F4\u63A5\u4EA4\u4E92");
702
+ return { element, didScroll };
703
+ }
704
+ } else {
705
+ samePositionCount = 0;
706
+ }
707
+ }
708
+ if (currentRect) lastRect = currentRect;
679
709
  if (status.code === "OBSTRUCTED" && status.obstruction) {
680
- logger5.debug(`humanScroll | \u88AB\u4EE5\u4E0B\u5143\u7D20\u906E\u6321 <${status.obstruction.tag} id="${status.obstruction.id}">`);
710
+ logger5.info(`humanScroll | \u88AB\u906E\u6321: <${status.obstruction.tag}.${status.obstruction.className}>`);
681
711
  }
682
712
  let deltaY = 0;
683
713
  if (status.code === "OUT_OF_VIEWPORT") {
@@ -690,22 +720,23 @@ var Humanize = {
690
720
  }
691
721
  } else if (status.code === "OBSTRUCTED") {
692
722
  const isBottomHalf = status.cy > status.viewH / 2;
693
- const direction = isBottomHalf ? 1 : -1;
694
- deltaY = direction * (minStep + Math.random() * 50);
723
+ const dir = isBottomHalf ? 1 : -1;
724
+ deltaY = dir * (minStep + Math.random() * 50);
695
725
  }
696
726
  if (i === 0 || Math.random() < 0.2) {
697
727
  const viewSize = page.viewportSize();
698
728
  if (viewSize) {
699
729
  const safeX = viewSize.width * 0.5 + (Math.random() - 0.5) * 100;
700
730
  const safeY = viewSize.height * 0.5 + (Math.random() - 0.5) * 100;
701
- await cursor.actions.move({ x: safeX, y: safeY });
731
+ await cursor.actions.move({ x: safeX, y: safeY }).catch(() => {
732
+ });
702
733
  }
703
734
  }
704
735
  await page.mouse.wheel(0, deltaY);
705
736
  didScroll = true;
706
- await (0, import_delay2.default)(this.jitterMs(100 + Math.random() * 150, 0.2));
737
+ await (0, import_delay2.default)(this.jitterMs(150 + Math.random() * 100, 0.2));
707
738
  }
708
- logger5.warn(`humanScroll | \u5728 ${maxSteps} \u6B65\u540E\u65E0\u6CD5\u786E\u4FDD\u53EF\u89C1\u6027`);
739
+ logger5.warn(`humanScroll | \u26A0\uFE0F \u8FBE\u5230\u6700\u5927\u6B65\u6570 (${maxSteps}) \u4ECD\u65E0\u6CD5\u5B8C\u5168\u53EF\u89C1\uFF0C\u653E\u5F03\u6EDA\u52A8`);
709
740
  return { element, didScroll };
710
741
  } catch (error) {
711
742
  logger5.fail("humanScroll", error);
@@ -917,34 +948,6 @@ var Humanize = {
917
948
  logger5.fail("warmUpBrowsing", error);
918
949
  throw error;
919
950
  }
920
- },
921
- /**
922
- * 自然滚动 - 带惯性、减速效果和随机抖动
923
- * @param {import('playwright').Page} page
924
- * @param {'up' | 'down'} [direction='down'] - 滚动方向
925
- * @param {number} [distance=300] - 总滚动距离基础值 (px),±15% 抖动
926
- * @param {number} [baseSteps=5] - 分几步完成基础值,±1 随机
927
- */
928
- async naturalScroll(page, direction = "down", distance = 300, baseSteps = 5) {
929
- const steps = Math.max(3, baseSteps + Math.floor(Math.random() * 3) - 1);
930
- const actualDistance = this.jitterMs(distance, 0.15);
931
- logger5.start("naturalScroll", `dir=${direction}, dist=${actualDistance}, steps=${steps}`);
932
- const sign = direction === "down" ? 1 : -1;
933
- const stepDistance = actualDistance / steps;
934
- try {
935
- for (let i = 0; i < steps; i++) {
936
- const factor = 1 - i / steps * 0.5;
937
- const jitter = 0.9 + Math.random() * 0.2;
938
- const scrollAmount = stepDistance * factor * sign * jitter;
939
- await page.mouse.wheel(0, scrollAmount);
940
- const baseDelay = 60 + i * 25;
941
- await (0, import_delay2.default)(this.jitterMs(baseDelay, 0.3));
942
- }
943
- logger5.success("naturalScroll");
944
- } catch (error) {
945
- logger5.fail("naturalScroll", error);
946
- throw error;
947
- }
948
951
  }
949
952
  };
950
953