@skrillex1224/playwright-toolkit 2.0.2 → 2.0.4
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.cjs +442 -0
- package/dist/index.cjs.map +7 -0
- package/dist/index.js +400 -9
- package/dist/index.js.map +7 -0
- package/package.json +18 -4
- package/dist/src/apify-kit.js +0 -96
- package/dist/src/constants.js +0 -21
- package/dist/src/humanize.js +0 -30
- package/dist/src/launch.js +0 -27
- package/dist/src/live-view.js +0 -64
- package/dist/src/stealth.js +0 -70
- package/dist/src/utils.js +0 -24
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
|
|
29
|
+
// index.js
|
|
30
|
+
var index_exports = {};
|
|
31
|
+
__export(index_exports, {
|
|
32
|
+
usePlaywrightToolKit: () => usePlaywrightToolKit
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(index_exports);
|
|
35
|
+
|
|
36
|
+
// src/optional-deps.js
|
|
37
|
+
var crawleeLog;
|
|
38
|
+
async function loadCrawlee() {
|
|
39
|
+
if (crawleeLog) return crawleeLog;
|
|
40
|
+
try {
|
|
41
|
+
const crawlee = await import("crawlee");
|
|
42
|
+
crawleeLog = crawlee.log;
|
|
43
|
+
} catch (e) {
|
|
44
|
+
crawleeLog = {
|
|
45
|
+
info: (...args) => console.log("[INFO]", ...args),
|
|
46
|
+
warning: (...args) => console.warn("[WARNING]", ...args),
|
|
47
|
+
error: (...args) => console.error("[ERROR]", ...args),
|
|
48
|
+
debug: (...args) => console.debug("[DEBUG]", ...args)
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return crawleeLog;
|
|
52
|
+
}
|
|
53
|
+
var log = new Proxy({}, {
|
|
54
|
+
get(target, prop) {
|
|
55
|
+
return (...args) => {
|
|
56
|
+
return loadCrawlee().then((logger) => {
|
|
57
|
+
if (typeof logger[prop] === "function") {
|
|
58
|
+
return logger[prop](...args);
|
|
59
|
+
}
|
|
60
|
+
return logger[prop];
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
var ApifyActor;
|
|
66
|
+
var isApifyAvailable = false;
|
|
67
|
+
async function loadApify() {
|
|
68
|
+
if (ApifyActor) return { Actor: ApifyActor, isApifyAvailable };
|
|
69
|
+
try {
|
|
70
|
+
const apify = await import("apify");
|
|
71
|
+
ApifyActor = apify.Actor;
|
|
72
|
+
isApifyAvailable = true;
|
|
73
|
+
} catch (e) {
|
|
74
|
+
ApifyActor = {
|
|
75
|
+
// 基础方法返回 null 或空操作
|
|
76
|
+
getValue: async () => null,
|
|
77
|
+
setValue: async () => {
|
|
78
|
+
},
|
|
79
|
+
pushData: async (data) => {
|
|
80
|
+
console.log("[APIFY_STUB] pushData called:", data);
|
|
81
|
+
},
|
|
82
|
+
isAtHome: () => false,
|
|
83
|
+
fail: async (message) => {
|
|
84
|
+
throw new Error(message);
|
|
85
|
+
},
|
|
86
|
+
// 降级提示
|
|
87
|
+
_isStub: true
|
|
88
|
+
};
|
|
89
|
+
isApifyAvailable = false;
|
|
90
|
+
}
|
|
91
|
+
return { Actor: ApifyActor, isApifyAvailable };
|
|
92
|
+
}
|
|
93
|
+
var Actor = new Proxy({}, {
|
|
94
|
+
get(target, prop) {
|
|
95
|
+
return (...args) => {
|
|
96
|
+
return loadApify().then(({ Actor: Actor2 }) => {
|
|
97
|
+
if (typeof Actor2[prop] === "function") {
|
|
98
|
+
return Actor2[prop](...args);
|
|
99
|
+
}
|
|
100
|
+
return Actor2[prop];
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// src/constants.js
|
|
107
|
+
var constants_exports = {};
|
|
108
|
+
__export(constants_exports, {
|
|
109
|
+
ErrorKeygen: () => ErrorKeygen,
|
|
110
|
+
FAILED_KEY_SEPARATOR: () => FAILED_KEY_SEPARATOR,
|
|
111
|
+
PresetOfLiveViewKey: () => PresetOfLiveViewKey,
|
|
112
|
+
Status: () => Status,
|
|
113
|
+
StatusCode: () => StatusCode
|
|
114
|
+
});
|
|
115
|
+
var ErrorKeygen = {
|
|
116
|
+
NotLogin: 30000001,
|
|
117
|
+
Chaptcha: 30000002
|
|
118
|
+
};
|
|
119
|
+
var Status = {
|
|
120
|
+
Success: "SUCCESS",
|
|
121
|
+
Failed: "FAILED"
|
|
122
|
+
};
|
|
123
|
+
var StatusCode = {
|
|
124
|
+
Success: 0,
|
|
125
|
+
Failed: -1
|
|
126
|
+
};
|
|
127
|
+
var FAILED_KEY_SEPARATOR = "::<@>::";
|
|
128
|
+
var PresetOfLiveViewKey = "LIVE_VIEW_SCREENSHOT";
|
|
129
|
+
|
|
130
|
+
// src/apify-kit.js
|
|
131
|
+
var ApifyKit = {
|
|
132
|
+
/**
|
|
133
|
+
* 包装 Step Name
|
|
134
|
+
*/
|
|
135
|
+
wrapStepNameWithFailedKey(key, stepName) {
|
|
136
|
+
return `${key}${FAILED_KEY_SEPARATOR}${stepName}`;
|
|
137
|
+
},
|
|
138
|
+
/**
|
|
139
|
+
* 解包 Step Name
|
|
140
|
+
*/
|
|
141
|
+
unwrapStepName(stepName) {
|
|
142
|
+
const splitIndex = stepName.indexOf(FAILED_KEY_SEPARATOR);
|
|
143
|
+
if (splitIndex === -1) {
|
|
144
|
+
return ["-", stepName];
|
|
145
|
+
}
|
|
146
|
+
const key = stepName.substring(0, splitIndex);
|
|
147
|
+
const value = stepName.substring(splitIndex + FAILED_KEY_SEPARATOR.length);
|
|
148
|
+
return [key, value];
|
|
149
|
+
},
|
|
150
|
+
/**
|
|
151
|
+
* 核心封装:执行步骤,带自动日志确认和失败截图处理
|
|
152
|
+
*/
|
|
153
|
+
async runStep(pendingStepName, page, actionFn, options = {}) {
|
|
154
|
+
const { failActor = true } = options;
|
|
155
|
+
const [failedKey, stepName] = this.unwrapStepName(pendingStepName);
|
|
156
|
+
log.info(`\u{1F504} [\u6B63\u5728\u6267\u884C] ${stepName}...`);
|
|
157
|
+
try {
|
|
158
|
+
const result = await actionFn();
|
|
159
|
+
log.info(`\u2705 [\u6267\u884C\u6210\u529F] ${stepName}`);
|
|
160
|
+
return result;
|
|
161
|
+
} catch (error) {
|
|
162
|
+
log.error(`\u274C [\u6267\u884C\u5931\u8D25] ${stepName}: ${error.message}`);
|
|
163
|
+
let screenshotBase64 = "\u622A\u56FE\u5931\u8D25";
|
|
164
|
+
try {
|
|
165
|
+
if (page) {
|
|
166
|
+
const buffer = await page.screenshot({ fullPage: true, type: "jpeg", quality: 60 });
|
|
167
|
+
screenshotBase64 = `data:image/jpeg;base64,${buffer.toString("base64")}`;
|
|
168
|
+
}
|
|
169
|
+
} catch (snapErr) {
|
|
170
|
+
log.warning(`\u622A\u56FE\u751F\u6210\u5931\u8D25: ${snapErr.message}`);
|
|
171
|
+
}
|
|
172
|
+
await this.pushFailed(error, {
|
|
173
|
+
failedStep: stepName,
|
|
174
|
+
failedKey,
|
|
175
|
+
errorMessage: error.message,
|
|
176
|
+
errorStack: error.stack,
|
|
177
|
+
screenshotBase64
|
|
178
|
+
});
|
|
179
|
+
if (failActor) {
|
|
180
|
+
await Actor.fail(`Run Step ${stepName} \u5931\u8D25: ${error.message}`);
|
|
181
|
+
} else {
|
|
182
|
+
throw error;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
/**
|
|
187
|
+
* 宽松版runStep:失败时不调用Actor.fail,只抛出异常
|
|
188
|
+
*/
|
|
189
|
+
async runStepLoose(stepName, page, fn) {
|
|
190
|
+
return await this.runStep(stepName, page, fn, { failActor: false });
|
|
191
|
+
},
|
|
192
|
+
/**
|
|
193
|
+
* 推送成功数据的通用方法
|
|
194
|
+
* @param {Object} data - 要推送的数据对象
|
|
195
|
+
*/
|
|
196
|
+
async pushSuccess(data) {
|
|
197
|
+
await Actor.pushData({
|
|
198
|
+
code: StatusCode.Success,
|
|
199
|
+
status: Status.Success,
|
|
200
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
201
|
+
...data
|
|
202
|
+
});
|
|
203
|
+
},
|
|
204
|
+
/**
|
|
205
|
+
* 推送失败数据的通用方法(私有方法,仅供runStep内部使用)
|
|
206
|
+
* @param {Error|Object} error - 错误对象(可包含其他的错误/或部分处理成功的额外信息)
|
|
207
|
+
* @param {Object} [meta] - 额外的数据(如failedStep, screenshotBase64等,仅runStep使用)
|
|
208
|
+
* @private
|
|
209
|
+
*/
|
|
210
|
+
async pushFailed(error, meta = {}) {
|
|
211
|
+
await Actor.pushData({
|
|
212
|
+
code: StatusCode.Failed,
|
|
213
|
+
status: Status.Failed,
|
|
214
|
+
// 这里可能带其他错误信息
|
|
215
|
+
error,
|
|
216
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
217
|
+
...meta
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// src/utils.js
|
|
223
|
+
var Utils = {
|
|
224
|
+
/**
|
|
225
|
+
* 解析 SSE 流文本
|
|
226
|
+
*/
|
|
227
|
+
parseSseStream(sseStreamText) {
|
|
228
|
+
const events = [];
|
|
229
|
+
const lines = sseStreamText.split("\n");
|
|
230
|
+
for (const line of lines) {
|
|
231
|
+
if (line.startsWith("data: ")) {
|
|
232
|
+
try {
|
|
233
|
+
const jsonContent = line.substring(6).trim();
|
|
234
|
+
if (jsonContent && jsonContent !== "[DONE]") {
|
|
235
|
+
events.push(JSON.parse(jsonContent));
|
|
236
|
+
}
|
|
237
|
+
} catch (e) {
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return events;
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
// src/stealth.js
|
|
246
|
+
var Stealth = {
|
|
247
|
+
/**
|
|
248
|
+
* 关键修复:将 Page 视口调整为与浏览器指纹 (window.screen) 一致。
|
|
249
|
+
* 防止 "Viewport Mismatch" 类型的反爬检测。
|
|
250
|
+
* @param {import('playwright').Page} page
|
|
251
|
+
*/
|
|
252
|
+
async syncViewportWithScreen(page) {
|
|
253
|
+
try {
|
|
254
|
+
const screen = await page.evaluate(() => ({
|
|
255
|
+
width: window.screen.width,
|
|
256
|
+
height: window.screen.height,
|
|
257
|
+
availWidth: window.screen.availWidth,
|
|
258
|
+
availHeight: window.screen.availHeight
|
|
259
|
+
}));
|
|
260
|
+
await page.setViewportSize({
|
|
261
|
+
width: screen.width,
|
|
262
|
+
height: screen.height
|
|
263
|
+
});
|
|
264
|
+
log.info(`[Stealth] Viewport synced to fingerprint: ${screen.width}x${screen.height}`);
|
|
265
|
+
} catch (e) {
|
|
266
|
+
log.warning(`[Stealth] Failed to sync viewport: ${e.message}. Fallback to 1920x1080.`);
|
|
267
|
+
await page.setViewportSize({ width: 1920, height: 1080 });
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
/**
|
|
271
|
+
* 确保 navigator.webdriver 隐藏 (通常 Playwright Stealth 插件已处理,但双重保险)
|
|
272
|
+
*/
|
|
273
|
+
async hideWebdriver(page) {
|
|
274
|
+
await page.addInitScript(() => {
|
|
275
|
+
Object.defineProperty(navigator, "webdriver", {
|
|
276
|
+
get: () => false
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
},
|
|
280
|
+
/**
|
|
281
|
+
* 通用的 Playwright 资源拦截器,用于屏蔽不必要的资源以加速加载
|
|
282
|
+
* @param {import('playwright').Page} page
|
|
283
|
+
* @param {string[]} [resourceTypes] - 要屏蔽的资源类型,默认为 ['font', 'image', 'media']
|
|
284
|
+
*/
|
|
285
|
+
async setupBlockingResources(page, resourceTypes = ["font", "image", "media"]) {
|
|
286
|
+
await page.route("**/*", (route) => {
|
|
287
|
+
const request = route.request();
|
|
288
|
+
const type = request.resourceType();
|
|
289
|
+
if (resourceTypes.includes(type)) {
|
|
290
|
+
return route.abort();
|
|
291
|
+
}
|
|
292
|
+
return route.continue();
|
|
293
|
+
});
|
|
294
|
+
},
|
|
295
|
+
/**
|
|
296
|
+
* 获取推荐的 Stealth 启动参数
|
|
297
|
+
*/
|
|
298
|
+
getStealthLaunchArgs() {
|
|
299
|
+
return [
|
|
300
|
+
"--disable-blink-features=AutomationControlled",
|
|
301
|
+
"--no-sandbox",
|
|
302
|
+
"--disable-setuid-sandbox",
|
|
303
|
+
"--disable-infobars",
|
|
304
|
+
"--window-position=0,0",
|
|
305
|
+
"--ignore-certificate-errors",
|
|
306
|
+
"--disable-web-security"
|
|
307
|
+
// 注意:不建议这里强制指定 window-size,让 syncViewportWithScreen 去动态调整
|
|
308
|
+
// '--window-size=1920,1080'
|
|
309
|
+
];
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
// src/humanize.js
|
|
314
|
+
var import_delay = __toESM(require("delay"), 1);
|
|
315
|
+
var Humanize = {
|
|
316
|
+
/**
|
|
317
|
+
* 随机延迟一段毫秒数 (API Wrapper for 'delay' package)
|
|
318
|
+
* @param {number} min - 最小毫秒
|
|
319
|
+
* @param {number} max - 最大毫秒
|
|
320
|
+
*/
|
|
321
|
+
async randomSleep(min, max) {
|
|
322
|
+
const ms = typeof max === "number" ? import_delay.default.range(min, max) : (0, import_delay.default)(min);
|
|
323
|
+
await ms;
|
|
324
|
+
},
|
|
325
|
+
/**
|
|
326
|
+
* 模拟人类“注视”或“阅读”行为:鼠标在页面上随机微动。
|
|
327
|
+
* @param {import('ghost-cursor-playwright').GhostCursor} cursor
|
|
328
|
+
* @param {number} durationMs - 持续时间
|
|
329
|
+
*/
|
|
330
|
+
async simulateGaze(cursor, durationMs = 2e3) {
|
|
331
|
+
const startTime = Date.now();
|
|
332
|
+
while (Date.now() - startTime < durationMs) {
|
|
333
|
+
const x = Math.random() * 800;
|
|
334
|
+
const y = Math.random() * 600;
|
|
335
|
+
await cursor.moveTo({ x, y });
|
|
336
|
+
await import_delay.default.range(200, 800);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
// src/launch.js
|
|
342
|
+
var Launch = {
|
|
343
|
+
getLaunchOptions(customArgs = []) {
|
|
344
|
+
return {
|
|
345
|
+
args: [
|
|
346
|
+
...Stealth.getStealthLaunchArgs(),
|
|
347
|
+
...customArgs
|
|
348
|
+
],
|
|
349
|
+
ignoreDefaultArgs: ["--enable-automation"]
|
|
350
|
+
};
|
|
351
|
+
},
|
|
352
|
+
/**
|
|
353
|
+
* 推荐的 Fingerprint Generator 选项
|
|
354
|
+
* 确保生成的是桌面端、较新的 Chrome,以匹配我们的脚本逻辑
|
|
355
|
+
*/
|
|
356
|
+
getFingerprintGeneratorOptions() {
|
|
357
|
+
return {
|
|
358
|
+
browsers: [{ name: "chrome", minVersion: 110 }],
|
|
359
|
+
devices: ["desktop"],
|
|
360
|
+
operatingSystems: ["windows", "linux"]
|
|
361
|
+
// 包含 Linux 兼容容器
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
// src/live-view.js
|
|
367
|
+
var import_express = __toESM(require("express"), 1);
|
|
368
|
+
async function startLiveViewServer(liveViewKey) {
|
|
369
|
+
const app = (0, import_express.default)();
|
|
370
|
+
app.get("/", async (req, res) => {
|
|
371
|
+
try {
|
|
372
|
+
const screenshotBuffer = await Actor.getValue(liveViewKey);
|
|
373
|
+
if (!screenshotBuffer) {
|
|
374
|
+
res.send('<html><head><meta http-equiv="refresh" content="2"></head><body>\u7B49\u5F85\u7B2C\u4E00\u4E2A\u5C4F\u5E55\u622A\u56FE...</body></html>');
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
const screenshotBase64 = screenshotBuffer.toString("base64");
|
|
378
|
+
res.send(`
|
|
379
|
+
<html>
|
|
380
|
+
<head>
|
|
381
|
+
<title>Live View (\u622A\u56FE)</title>
|
|
382
|
+
<meta http-equiv="refresh" content="1">
|
|
383
|
+
</head>
|
|
384
|
+
<body style="margin:0; padding:0;">
|
|
385
|
+
<img src="data:image/png;base64,${screenshotBase64}"
|
|
386
|
+
alt="Live View Screenshot"
|
|
387
|
+
style="width: 100%; height: auto;" />
|
|
388
|
+
</body>
|
|
389
|
+
</html>
|
|
390
|
+
`);
|
|
391
|
+
} catch (error) {
|
|
392
|
+
log.error(`Live View \u670D\u52A1\u5668\u9519\u8BEF: ${error.message}`);
|
|
393
|
+
res.status(500).send(`\u65E0\u6CD5\u52A0\u8F7D\u5C4F\u5E55\u622A\u56FE: ${error.message}`);
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
const port = process.env.APIFY_CONTAINER_PORT || 4321;
|
|
397
|
+
app.listen(port, () => {
|
|
398
|
+
log.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`);
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
async function takeLiveScreenshot(liveViewKey, page, logMessage) {
|
|
402
|
+
try {
|
|
403
|
+
const buffer = await page.screenshot({ type: "png" });
|
|
404
|
+
await Actor.setValue(liveViewKey, buffer, { contentType: "image/png" });
|
|
405
|
+
if (logMessage) {
|
|
406
|
+
log.info(`(\u622A\u56FE): ${logMessage}`);
|
|
407
|
+
}
|
|
408
|
+
} catch (e) {
|
|
409
|
+
log.warning(`\u65E0\u6CD5\u6355\u83B7 Live View \u5C4F\u5E55\u622A\u56FE: ${e.message}`);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
var useLiveView = (liveViewKey = PresetOfLiveViewKey) => {
|
|
413
|
+
return {
|
|
414
|
+
takeLiveScreenshot: async (page, logMessage) => {
|
|
415
|
+
return await takeLiveScreenshot(liveViewKey, page, logMessage);
|
|
416
|
+
},
|
|
417
|
+
startLiveViewServer: async () => {
|
|
418
|
+
return await startLiveViewServer(liveViewKey);
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
};
|
|
422
|
+
var LiveView = {
|
|
423
|
+
useLiveView
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
// index.js
|
|
427
|
+
var usePlaywrightToolKit = () => {
|
|
428
|
+
return {
|
|
429
|
+
ApifyKit,
|
|
430
|
+
Stealth,
|
|
431
|
+
Humanize,
|
|
432
|
+
Launch,
|
|
433
|
+
LiveView,
|
|
434
|
+
Constants: constants_exports,
|
|
435
|
+
Utils
|
|
436
|
+
};
|
|
437
|
+
};
|
|
438
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
439
|
+
0 && (module.exports = {
|
|
440
|
+
usePlaywrightToolKit
|
|
441
|
+
});
|
|
442
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../index.js", "../src/optional-deps.js", "../src/constants.js", "../src/apify-kit.js", "../src/utils.js", "../src/stealth.js", "../src/humanize.js", "../src/launch.js", "../src/live-view.js"],
|
|
4
|
+
"sourcesContent": ["import { ApifyKit } from './src/apify-kit.js';\nimport { Utils } from './src/utils.js';\nimport { Stealth } from './src/stealth.js';\nimport { Humanize } from './src/humanize.js';\nimport { Launch } from './src/launch.js';\nimport { LiveView } from './src/live-view.js';\nimport * as Constants from './src/constants.js';\n\n// Unified Entry Point\nexport const usePlaywrightToolKit = () => {\n return {\n ApifyKit,\n Stealth,\n Humanize,\n Launch,\n LiveView,\n Constants,\n Utils\n };\n};\n", "/**\n * \u53EF\u9009\u4F9D\u8D56\u52A0\u8F7D\u6A21\u5757\n * \u7528\u4E8E\u5904\u7406 apify \u548C crawlee \u7684\u6761\u4EF6\u5BFC\u5165\uFF0C\u652F\u6301\u672C\u5730\u548C Apify \u73AF\u5883\n */\n\n// ============ Crawlee Log \u964D\u7EA7\u65B9\u6848 ============\nlet crawleeLog;\n\nasync function loadCrawlee() {\n if (crawleeLog) return crawleeLog;\n\n try {\n const crawlee = await import('crawlee');\n crawleeLog = crawlee.log;\n } catch (e) {\n // \u5982\u679C crawlee \u672A\u5B89\u88C5\uFF0C\u4F7F\u7528 console.log \u964D\u7EA7\n crawleeLog = {\n info: (...args) => console.log('[INFO]', ...args),\n warning: (...args) => console.warn('[WARNING]', ...args),\n error: (...args) => console.error('[ERROR]', ...args),\n debug: (...args) => console.debug('[DEBUG]', ...args),\n };\n }\n return crawleeLog;\n}\n\n// \u540C\u6B65\u4EE3\u7406\uFF0C\u5EF6\u8FDF\u52A0\u8F7D\nexport const log = new Proxy({}, {\n get(target, prop) {\n return (...args) => {\n return loadCrawlee().then(logger => {\n if (typeof logger[prop] === 'function') {\n return logger[prop](...args);\n }\n return logger[prop];\n });\n };\n }\n});\n\n// ============ Apify Actor \u964D\u7EA7\u65B9\u6848 ============\nlet ApifyActor;\nlet isApifyAvailable = false;\n\nasync function loadApify() {\n if (ApifyActor) return { Actor: ApifyActor, isApifyAvailable };\n\n try {\n const apify = await import('apify');\n ApifyActor = apify.Actor;\n isApifyAvailable = true;\n } catch (e) {\n // \u5982\u679C apify \u672A\u5B89\u88C5\uFF0C\u63D0\u4F9B\u964D\u7EA7\u5B9E\u73B0\n ApifyActor = {\n // \u57FA\u7840\u65B9\u6CD5\u8FD4\u56DE null \u6216\u7A7A\u64CD\u4F5C\n getValue: async () => null,\n setValue: async () => { },\n pushData: async (data) => {\n console.log('[APIFY_STUB] pushData called:', data);\n },\n isAtHome: () => false,\n fail: async (message) => {\n throw new Error(message);\n },\n\n // \u964D\u7EA7\u63D0\u793A\n _isStub: true,\n };\n isApifyAvailable = false;\n }\n\n return { Actor: ApifyActor, isApifyAvailable };\n}\n\n// \u540C\u6B65\u4EE3\u7406\uFF0C\u5EF6\u8FDF\u52A0\u8F7D\nexport const Actor = new Proxy({}, {\n get(target, prop) {\n return (...args) => {\n return loadApify().then(({ Actor }) => {\n if (typeof Actor[prop] === 'function') {\n return Actor[prop](...args);\n }\n return Actor[prop];\n });\n };\n }\n});\n\nexport { isApifyAvailable };\n", "export const ErrorKeygen = {\n NotLogin: 30000001,\n Chaptcha: 30000002,\n}\n\nexport const Status = {\n Success: 'SUCCESS',\n Failed: 'FAILED'\n}\n\nexport const StatusCode = {\n Success: 0,\n Failed: -1\n}\n\nexport const FAILED_KEY_SEPARATOR = '::<@>::';\n\nexport const PresetOfLiveViewKey = 'LIVE_VIEW_SCREENSHOT';\n", "import { log, Actor } from './optional-deps.js';\nimport { Status, FAILED_KEY_SEPARATOR, StatusCode } from './constants.js';\n\nexport const ApifyKit = {\n\n /**\n * \u5305\u88C5 Step Name\n */\n wrapStepNameWithFailedKey(key, stepName) {\n return `${key}${FAILED_KEY_SEPARATOR}${stepName}`;\n },\n\n /**\n * \u89E3\u5305 Step Name\n */\n unwrapStepName(stepName) {\n const splitIndex = stepName.indexOf(FAILED_KEY_SEPARATOR);\n if (splitIndex === -1) {\n return ['-', stepName];\n }\n const key = stepName.substring(0, splitIndex);\n const value = stepName.substring(splitIndex + FAILED_KEY_SEPARATOR.length);\n return [key, value];\n },\n\n /**\n * \u6838\u5FC3\u5C01\u88C5\uFF1A\u6267\u884C\u6B65\u9AA4\uFF0C\u5E26\u81EA\u52A8\u65E5\u5FD7\u786E\u8BA4\u548C\u5931\u8D25\u622A\u56FE\u5904\u7406\n */\n async runStep(pendingStepName, page, actionFn, options = {}) {\n const { failActor = true } = options; // \u9ED8\u8BA4\u8C03\u7528 Actor.fail\n const [failedKey, stepName] = this.unwrapStepName(pendingStepName);\n\n log.info(`\uD83D\uDD04 [\u6B63\u5728\u6267\u884C] ${stepName}...`);\n\n try {\n const result = await actionFn();\n log.info(`\u2705 [\u6267\u884C\u6210\u529F] ${stepName}`);\n return result;\n } catch (error) {\n log.error(`\u274C [\u6267\u884C\u5931\u8D25] ${stepName}: ${error.message}`);\n\n let screenshotBase64 = '\u622A\u56FE\u5931\u8D25';\n try {\n if (page) {\n const buffer = await page.screenshot({ fullPage: true, type: 'jpeg', quality: 60 });\n screenshotBase64 = `data:image/jpeg;base64,${buffer.toString('base64')}`;\n }\n } catch (snapErr) {\n log.warning(`\u622A\u56FE\u751F\u6210\u5931\u8D25: ${snapErr.message}`);\n }\n\n // \u4F7F\u7528 pushFailed \u65B9\u6CD5\u63A8\u9001\u5931\u8D25\u6570\u636E\uFF08\u79C1\u6709\u4F7F\u7528\uFF09\n await this.pushFailed(error, {\n failedStep: stepName,\n failedKey: failedKey,\n errorMessage: error.message,\n errorStack: error.stack,\n screenshotBase64: screenshotBase64\n });\n\n // \u6839\u636E failActor \u51B3\u5B9A\u662F\u5426\u8C03\u7528 Actor.fail\n if (failActor) {\n await Actor.fail(`Run Step ${stepName} \u5931\u8D25: ${error.message}`);\n } else {\n // \u4E0D\u8C03\u7528 Actor.fail\uFF0C\u76F4\u63A5\u629B\u51FA\u9519\u8BEF\n throw error;\n }\n }\n },\n\n /**\n * \u5BBD\u677E\u7248runStep\uFF1A\u5931\u8D25\u65F6\u4E0D\u8C03\u7528Actor.fail\uFF0C\u53EA\u629B\u51FA\u5F02\u5E38\n */\n async runStepLoose(stepName, page, fn) {\n return await this.runStep(stepName, page, fn, { failActor: false });\n },\n\n /**\n * \u63A8\u9001\u6210\u529F\u6570\u636E\u7684\u901A\u7528\u65B9\u6CD5\n * @param {Object} data - \u8981\u63A8\u9001\u7684\u6570\u636E\u5BF9\u8C61\n */\n async pushSuccess(data) {\n await Actor.pushData({\n code: StatusCode.Success,\n status: Status.Success,\n timestamp: new Date().toISOString(),\n ...data\n });\n },\n\n /**\n * \u63A8\u9001\u5931\u8D25\u6570\u636E\u7684\u901A\u7528\u65B9\u6CD5\uFF08\u79C1\u6709\u65B9\u6CD5\uFF0C\u4EC5\u4F9BrunStep\u5185\u90E8\u4F7F\u7528\uFF09\n * @param {Error|Object} error - \u9519\u8BEF\u5BF9\u8C61\uFF08\u53EF\u5305\u542B\u5176\u4ED6\u7684\u9519\u8BEF/\u6216\u90E8\u5206\u5904\u7406\u6210\u529F\u7684\u989D\u5916\u4FE1\u606F\uFF09\n * @param {Object} [meta] - \u989D\u5916\u7684\u6570\u636E\uFF08\u5982failedStep, screenshotBase64\u7B49\uFF0C\u4EC5runStep\u4F7F\u7528\uFF09\n * @private\n */\n async pushFailed(error, meta = {}) {\n await Actor.pushData({\n code: StatusCode.Failed,\n status: Status.Failed,\n // \u8FD9\u91CC\u53EF\u80FD\u5E26\u5176\u4ED6\u9519\u8BEF\u4FE1\u606F\n error,\n timestamp: new Date().toISOString(),\n ...meta\n });\n }\n}\n", "export const Utils = {\n /**\n * \u89E3\u6790 SSE \u6D41\u6587\u672C\n */\n parseSseStream(sseStreamText) {\n const events = [];\n const lines = sseStreamText.split('\\n');\n for (const line of lines) {\n if (line.startsWith('data: ')) {\n try {\n const jsonContent = line.substring(6).trim();\n if (jsonContent && jsonContent !== '[DONE]') {\n events.push(JSON.parse(jsonContent));\n }\n } catch (e) {\n // Ignore lines that are not valid JSON\n }\n }\n }\n return events;\n }\n}\n", "import { log } from './optional-deps.js';\n\nexport const Stealth = {\n /**\n * \u5173\u952E\u4FEE\u590D\uFF1A\u5C06 Page \u89C6\u53E3\u8C03\u6574\u4E3A\u4E0E\u6D4F\u89C8\u5668\u6307\u7EB9 (window.screen) \u4E00\u81F4\u3002\n * \u9632\u6B62 \"Viewport Mismatch\" \u7C7B\u578B\u7684\u53CD\u722C\u68C0\u6D4B\u3002\n * @param {import('playwright').Page} page \n */\n async syncViewportWithScreen(page) {\n try {\n // \u83B7\u53D6\u6307\u7EB9\u4E2D\u7684\u5C4F\u5E55\u5C3A\u5BF8\n const screen = await page.evaluate(() => ({\n width: window.screen.width,\n height: window.screen.height,\n availWidth: window.screen.availWidth,\n availHeight: window.screen.availHeight,\n }));\n\n // \u8C03\u6574\u89C6\u53E3\n await page.setViewportSize({\n width: screen.width,\n height: screen.height\n });\n\n log.info(`[Stealth] Viewport synced to fingerprint: ${screen.width}x${screen.height}`);\n } catch (e) {\n log.warning(`[Stealth] Failed to sync viewport: ${e.message}. Fallback to 1920x1080.`);\n await page.setViewportSize({ width: 1920, height: 1080 });\n }\n },\n\n /**\n * \u786E\u4FDD navigator.webdriver \u9690\u85CF (\u901A\u5E38 Playwright Stealth \u63D2\u4EF6\u5DF2\u5904\u7406\uFF0C\u4F46\u53CC\u91CD\u4FDD\u9669)\n */\n async hideWebdriver(page) {\n await page.addInitScript(() => {\n Object.defineProperty(navigator, 'webdriver', {\n get: () => false,\n });\n });\n },\n\n /**\n * \u901A\u7528\u7684 Playwright \u8D44\u6E90\u62E6\u622A\u5668\uFF0C\u7528\u4E8E\u5C4F\u853D\u4E0D\u5FC5\u8981\u7684\u8D44\u6E90\u4EE5\u52A0\u901F\u52A0\u8F7D\n * @param {import('playwright').Page} page\n * @param {string[]} [resourceTypes] - \u8981\u5C4F\u853D\u7684\u8D44\u6E90\u7C7B\u578B\uFF0C\u9ED8\u8BA4\u4E3A ['font', 'image', 'media']\n */\n async setupBlockingResources(page, resourceTypes = ['font', 'image', 'media']) {\n await page.route('**/*', (route) => {\n const request = route.request();\n const type = request.resourceType();\n if (resourceTypes.includes(type)) {\n return route.abort();\n }\n return route.continue();\n });\n },\n\n /**\n * \u83B7\u53D6\u63A8\u8350\u7684 Stealth \u542F\u52A8\u53C2\u6570\n */\n getStealthLaunchArgs() {\n return [\n '--disable-blink-features=AutomationControlled',\n '--no-sandbox',\n '--disable-setuid-sandbox',\n '--disable-infobars',\n '--window-position=0,0',\n '--ignore-certificate-errors',\n '--disable-web-security',\n // \u6CE8\u610F\uFF1A\u4E0D\u5EFA\u8BAE\u8FD9\u91CC\u5F3A\u5236\u6307\u5B9A window-size\uFF0C\u8BA9 syncViewportWithScreen \u53BB\u52A8\u6001\u8C03\u6574\n // '--window-size=1920,1080' \n ];\n }\n}\n", "import delay from 'delay';\nimport { log } from './optional-deps.js';\n\nexport const Humanize = {\n /**\n * \u968F\u673A\u5EF6\u8FDF\u4E00\u6BB5\u6BEB\u79D2\u6570 (API Wrapper for 'delay' package)\n * @param {number} min - \u6700\u5C0F\u6BEB\u79D2\n * @param {number} max - \u6700\u5927\u6BEB\u79D2\n */\n async randomSleep(min, max) {\n const ms = typeof max === 'number'\n ? delay.range(min, max)\n : delay(min); // \u5982\u679C\u53EA\u4F20\u4E00\u4E2A\u53C2\u6570\uFF0C\u89C6\u4E3A\u56FA\u5B9A\u5EF6\u8FDF\u6216\u6700\u5C0F\u5EF6\u8FDF\n\n // log.debug(`[Humanize] Sleeping for ${await ms} ms...`); // delay return promise acts like number somewhat but best await it\n // The delay package returns a promise that resolves after the delay.\n // delay.range() returns a promise too.\n\n await ms;\n },\n\n /**\n * \u6A21\u62DF\u4EBA\u7C7B\u201C\u6CE8\u89C6\u201D\u6216\u201C\u9605\u8BFB\u201D\u884C\u4E3A\uFF1A\u9F20\u6807\u5728\u9875\u9762\u4E0A\u968F\u673A\u5FAE\u52A8\u3002\n * @param {import('ghost-cursor-playwright').GhostCursor} cursor \n * @param {number} durationMs - \u6301\u7EED\u65F6\u95F4\n */\n async simulateGaze(cursor, durationMs = 2000) {\n const startTime = Date.now();\n while (Date.now() - startTime < durationMs) {\n // \u968F\u673A\u5C0F\u5E45\u5EA6\u79FB\u52A8\n const x = Math.random() * 800;\n const y = Math.random() * 600;\n await cursor.moveTo({ x, y });\n await delay.range(200, 800);\n }\n }\n}\n", "// \u96C6\u4E2D\u7BA1\u7406\u542F\u52A8\u914D\u7F6E\uFF0C\u6682\u65F6\u4E3B\u8981\u7531 Stealth \u6A21\u5757\u63D0\u4F9B Args\uFF0C\u8FD9\u91CC\u4F5C\u4E3A\u6269\u5C55\u70B9\nimport { Stealth } from './stealth.js';\n\nexport const Launch = {\n getLaunchOptions(customArgs = []) {\n return {\n args: [\n ...Stealth.getStealthLaunchArgs(),\n ...customArgs\n ],\n ignoreDefaultArgs: ['--enable-automation'],\n };\n },\n\n /**\n * \u63A8\u8350\u7684 Fingerprint Generator \u9009\u9879\n * \u786E\u4FDD\u751F\u6210\u7684\u662F\u684C\u9762\u7AEF\u3001\u8F83\u65B0\u7684 Chrome\uFF0C\u4EE5\u5339\u914D\u6211\u4EEC\u7684\u811A\u672C\u903B\u8F91\n */\n getFingerprintGeneratorOptions() {\n return {\n browsers: [{ name: 'chrome', minVersion: 110 }],\n devices: ['desktop'],\n operatingSystems: ['windows', 'linux'], // \u5305\u542B Linux \u517C\u5BB9\u5BB9\u5668\n };\n }\n}\n", "import express from 'express';\nimport { log, Actor } from './optional-deps.js';\nimport { PresetOfLiveViewKey } from './constants.js';\n\n/**\n * \u542F\u52A8\u4E00\u4E2A Web \u670D\u52A1\u5668\u4EE5\u5728 Live View \u9009\u9879\u5361\u4E2D\u663E\u793A\u6700\u65B0\u7684\u5C4F\u5E55\u622A\u56FE\u3002\n */\nasync function startLiveViewServer(liveViewKey) {\n const app = express();\n\n app.get('/', async (req, res) => {\n try {\n // \u4ECE\u9ED8\u8BA4\u7684 Key-Value Store \u4E2D\u8BFB\u53D6\u6700\u65B0\u7684\u5C4F\u5E55\u622A\u56FE\n const screenshotBuffer = await Actor.getValue(liveViewKey);\n\n if (!screenshotBuffer) {\n // \u5982\u679C\u8FD8\u6CA1\u6709\u622A\u56FE\uFF0C\u53D1\u9001\u4E00\u4E2A\u81EA\u52A8\u5237\u65B0\u7684\u5360\u4F4D\u9875\u9762\n res.send('<html><head><meta http-equiv=\"refresh\" content=\"2\"></head><body>\u7B49\u5F85\u7B2C\u4E00\u4E2A\u5C4F\u5E55\u622A\u56FE...</body></html>');\n return;\n }\n\n // \u5C06 Buffer \u8F6C\u6362\u4E3A Base64 \u5B57\u7B26\u4E32\n const screenshotBase64 = screenshotBuffer.toString('base64');\n\n // \u53D1\u9001\u4E00\u4E2A HTML \u9875\u9762\uFF0C\u8BE5\u9875\u9762\u6BCF 1 \u79D2\u81EA\u52A8\u5237\u65B0\u4E00\u6B21\uFF0C\u5E76\u663E\u793A\u622A\u56FE\n res.send(`\n <html>\n <head>\n <title>Live View (\u622A\u56FE)</title>\n <meta http-equiv=\"refresh\" content=\"1\">\n </head>\n <body style=\"margin:0; padding:0;\">\n <img src=\"data:image/png;base64,${screenshotBase64}\" \n alt=\"Live View Screenshot\" \n style=\"width: 100%; height: auto;\" />\n </body>\n </html>\n `);\n } catch (error) {\n log.error(`Live View \u670D\u52A1\u5668\u9519\u8BEF: ${error.message}`);\n res.status(500).send(`\u65E0\u6CD5\u52A0\u8F7D\u5C4F\u5E55\u622A\u56FE: ${error.message}`);\n }\n });\n\n // \u76D1\u542C Apify \u5BB9\u5668\u7AEF\u53E3 \n const port = process.env.APIFY_CONTAINER_PORT || 4321;\n app.listen(port, () => { log.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`); });\n}\n\n/**\n * \u62CD\u6444\u5F53\u524D\u9875\u9762\u7684\u5C4F\u5E55\u622A\u56FE\u5E76\u5C06\u5176\u4FDD\u5B58\u5230 Key-Value Store\u3002\n * @param {import('playwright').Page} page\n * @param {string} [logMessage] - \u53EF\u9009\u7684\u65E5\u5FD7\u6D88\u606F\u3002\n */\nasync function takeLiveScreenshot(liveViewKey, page, logMessage) {\n try {\n const buffer = await page.screenshot({ type: 'png' });\n await Actor.setValue(liveViewKey, buffer, { contentType: 'image/png' });\n if (logMessage) {\n log.info(`(\u622A\u56FE): ${logMessage}`);\n }\n } catch (e) {\n log.warning(`\u65E0\u6CD5\u6355\u83B7 Live View \u5C4F\u5E55\u622A\u56FE: ${e.message}`);\n }\n}\n\nconst useLiveView = (liveViewKey = PresetOfLiveViewKey) => {\n return {\n takeLiveScreenshot: async (page, logMessage) => {\n return await takeLiveScreenshot(liveViewKey, page, logMessage)\n },\n startLiveViewServer: async () => {\n return await startLiveViewServer(liveViewKey);\n }\n }\n}\n\nexport const LiveView = {\n useLiveView,\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["Actor", "delay", "express"]
|
|
7
|
+
}
|