@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 +17 -18
- package/dist/index.cjs +47 -44
- package/dist/index.cjs.map +2 -2
- package/dist/index.js +47 -44
- package/dist/index.js.map +2 -2
- package/index.d.ts +0 -1
- package/package.json +1 -1
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?)`
|
|
92
|
-
| `AntiCheat` | `syncViewportWithScreen(page)`
|
|
93
|
-
| `AntiCheat` | `getTlsFingerprintOptions(userAgent?)`
|
|
94
|
-
| `Humanize` | `initializeCursor(page)`
|
|
95
|
-
| `Humanize` | `jitterMs(base, jitterPercent?)`
|
|
96
|
-
| `Humanize` | `humanType(page, selector, text, options?)`
|
|
97
|
-
| `Humanize` | `humanClick(page, selector, options?)`
|
|
98
|
-
| `Humanize` | `warmUpBrowsing(page, baseDuration?)`
|
|
99
|
-
| `Humanize` | `
|
|
100
|
-
| `Humanize` | `
|
|
101
|
-
| `
|
|
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.
|
|
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
|
|
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.
|
|
688
|
+
logger5.info("humanScroll | \u5143\u7D20\u5DF2\u53EF\u89C1\u4E14\u65E0\u906E\u6321");
|
|
676
689
|
return { element, didScroll };
|
|
677
690
|
}
|
|
678
|
-
|
|
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.
|
|
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
|
|
694
|
-
deltaY =
|
|
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(
|
|
737
|
+
await (0, import_delay2.default)(this.jitterMs(150 + Math.random() * 100, 0.2));
|
|
707
738
|
}
|
|
708
|
-
logger5.warn(`humanScroll | \
|
|
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
|
|