nodejs_chromium 1.1.15 → 1.1.16
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/index.js +14 -5
- package/package.json +1 -1
- package/src/chrome.js +162 -70
package/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require('nodejs_patch');
|
|
2
2
|
const puppeteer = require("puppeteer");
|
|
3
|
+
const { KnownDevices } = require("puppeteer");
|
|
3
4
|
const chrome = require("./src/chrome.js");
|
|
4
5
|
const { exec } = require("child_process");
|
|
5
6
|
global.__UA__ = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36';
|
|
@@ -16,13 +17,17 @@ async function NormalClose(path) {
|
|
|
16
17
|
*/
|
|
17
18
|
let file = `${path}/Default/Preferences`;
|
|
18
19
|
// console.log({file});
|
|
19
|
-
if (!fs.existsSync(file))
|
|
20
|
+
if (!fs.existsSync(file)) {
|
|
21
|
+
console.log(file, 'NotExists')
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
20
24
|
let text = fs.readFileSync(file);
|
|
21
25
|
let json = JSON.parse(text);
|
|
22
26
|
json.credentials_enable_service = false; //新增项,禁止保存密码
|
|
23
27
|
json.profile.exit_type = 'Normal'; //原值可能为:Crashed
|
|
24
28
|
// console.log('json.profile.exit_type', json.profile.exit_type);
|
|
25
29
|
fs.writeFileSync(file, JSON.stringify(json));
|
|
30
|
+
console.log(file, json.profile.exit_type)
|
|
26
31
|
}
|
|
27
32
|
|
|
28
33
|
|
|
@@ -99,7 +104,7 @@ async function newBrowser(params) {
|
|
|
99
104
|
|
|
100
105
|
const browser = await puppeteer.launch(option);
|
|
101
106
|
|
|
102
|
-
await NormalClose(option.userDataDir); //禁止显示【要恢复页面吗?chromium 未正确关闭。】
|
|
107
|
+
// await NormalClose(option.userDataDir); //禁止显示【要恢复页面吗?chromium 未正确关闭。】
|
|
103
108
|
|
|
104
109
|
return browser;
|
|
105
110
|
}
|
|
@@ -126,6 +131,7 @@ async function newChrome(params) {
|
|
|
126
131
|
cookies = null, //若cookies=false,则不处理cookies,不指定则由chrome处理,若=文件路径
|
|
127
132
|
abort = [], //要禁止的类型,常见["font","image","ping","stylesheet","document","fetch","script","xhr"]
|
|
128
133
|
headers = {},
|
|
134
|
+
device = null
|
|
129
135
|
} = params;
|
|
130
136
|
|
|
131
137
|
const browser = await newBrowser(params);
|
|
@@ -136,11 +142,14 @@ async function newChrome(params) {
|
|
|
136
142
|
// const version = await browser.version();
|
|
137
143
|
const page = (await browser.pages())[0];
|
|
138
144
|
// const page = await browser.newPage();
|
|
139
|
-
await page.setRequestInterception(true); //允许拦截
|
|
145
|
+
// await page.setRequestInterception(true); //允许拦截
|
|
140
146
|
|
|
141
147
|
if (ua) await page.setUserAgent(ua);
|
|
142
|
-
else if (mobile)
|
|
148
|
+
else if (mobile) {
|
|
149
|
+
await page.emulate(device || KnownDevices['iPhone 15 Pro']);
|
|
150
|
+
}
|
|
143
151
|
else await page.setUserAgent(__UA__);
|
|
152
|
+
if (device) await page.emulate(device); //必须要放在setUserAgent后面
|
|
144
153
|
|
|
145
154
|
await page.evaluateOnNewDocument(() => {
|
|
146
155
|
const newProto = navigator.__proto__;
|
|
@@ -149,7 +158,7 @@ async function newChrome(params) {
|
|
|
149
158
|
});
|
|
150
159
|
|
|
151
160
|
if (cookies !== false) cookies = `runtime/.cache/${id}/cookies.json`;
|
|
152
|
-
const pageOption = { cookies, visible, abort, headers };
|
|
161
|
+
const pageOption = { cookies, visible, abort, headers, device };
|
|
153
162
|
return new chrome(browser, page, pageOption);
|
|
154
163
|
}
|
|
155
164
|
catch (err) {
|
package/package.json
CHANGED
package/src/chrome.js
CHANGED
|
@@ -2,7 +2,6 @@ const fs = require("fs");
|
|
|
2
2
|
const Cookies = require("./cookies");
|
|
3
3
|
const { URL } = require('url');
|
|
4
4
|
|
|
5
|
-
|
|
6
5
|
/**
|
|
7
6
|
*
|
|
8
7
|
* class CdpFrame extends _classSuper
|
|
@@ -19,8 +18,10 @@ module.exports = class {
|
|
|
19
18
|
requestCall = void 0;
|
|
20
19
|
cookies = void 0;
|
|
21
20
|
options = void 0;
|
|
21
|
+
device = void 0;
|
|
22
22
|
isFrame = false; //是不是在iFrame中
|
|
23
23
|
visible = false; //是否可见,也就是有没有启动窗口
|
|
24
|
+
resIndex = 0; //当前窗口经过response的序号
|
|
24
25
|
|
|
25
26
|
constructor(browser, page, options, isFrame = false) {
|
|
26
27
|
this.browser = browser;
|
|
@@ -28,28 +29,78 @@ module.exports = class {
|
|
|
28
29
|
this.options = options;
|
|
29
30
|
this.cookies = new Cookies(page, options.cookies);
|
|
30
31
|
this.visible = !!options.visible;
|
|
32
|
+
this.device = options.device;
|
|
31
33
|
this.isFrame = !!isFrame;
|
|
32
34
|
this.doListening(options);
|
|
33
35
|
}
|
|
34
36
|
|
|
35
|
-
|
|
37
|
+
/**
|
|
38
|
+
* 设置拦截
|
|
39
|
+
* 如果只是要读取cookies这些信息而不拦截处理,只需调用interception
|
|
40
|
+
*
|
|
41
|
+
* @param call
|
|
42
|
+
* @returns {Promise<exports>}
|
|
43
|
+
*/
|
|
44
|
+
async request(call) {
|
|
45
|
+
|
|
46
|
+
if (call === true) {
|
|
47
|
+
//等同于this.interception(true)
|
|
48
|
+
await this.page.setRequestInterception(true); //允许拦截
|
|
49
|
+
this.page.interception = true;
|
|
50
|
+
this.requestCall = null;
|
|
51
|
+
return this;
|
|
52
|
+
|
|
53
|
+
}
|
|
54
|
+
else if (call === false) {
|
|
55
|
+
//等同于this.interception(false)
|
|
56
|
+
await this.page.setRequestInterception(false); //关闭允许拦截
|
|
57
|
+
this.page.interception = false;
|
|
58
|
+
this.requestCall = null;
|
|
59
|
+
return this;
|
|
60
|
+
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
await this.page.setRequestInterception(true); //允许拦截
|
|
64
|
+
this.page.interception = true;
|
|
36
65
|
this.requestCall = call;
|
|
37
66
|
return this;
|
|
38
67
|
}
|
|
39
68
|
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 设置是否允许拦截
|
|
72
|
+
*
|
|
73
|
+
* @param val
|
|
74
|
+
* @returns {Promise<exports>}
|
|
75
|
+
*/
|
|
76
|
+
async interception(val = true) {
|
|
77
|
+
await this.page.setRequestInterception(val);
|
|
78
|
+
this.page.interception = val;
|
|
79
|
+
return this;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
40
83
|
response(call) {
|
|
41
84
|
this.responseCall = call;
|
|
42
85
|
return this;
|
|
43
86
|
}
|
|
44
87
|
|
|
88
|
+
newPage(device = null) {
|
|
89
|
+
return this.clone(device);
|
|
90
|
+
}
|
|
91
|
+
|
|
45
92
|
/**
|
|
46
93
|
* 重新在当前浏览器创建新窗口
|
|
47
94
|
*/
|
|
48
|
-
async clone() {
|
|
95
|
+
async clone(device = null) {
|
|
49
96
|
try {
|
|
50
97
|
const page = await this.browser.newPage();
|
|
51
98
|
await page.setBypassCSP(true); //绕过页面的内容安全策略
|
|
52
|
-
await page.setRequestInterception(true); //允许拦截
|
|
99
|
+
// await page.setRequestInterception(true); //允许拦截
|
|
100
|
+
|
|
101
|
+
if (device) await page.emulate(device);
|
|
102
|
+
else if (this.options.device) await page.emulate(this.options.device);
|
|
103
|
+
|
|
53
104
|
await page.evaluateOnNewDocument(() => {
|
|
54
105
|
const newProto = navigator.__proto__;
|
|
55
106
|
delete newProto.webdriver; //删除 navigator.webdriver字段
|
|
@@ -108,10 +159,10 @@ module.exports = class {
|
|
|
108
159
|
/**
|
|
109
160
|
* 这必须放在已经打开过这个URL之后
|
|
110
161
|
*/
|
|
111
|
-
waitForFrame() {
|
|
162
|
+
waitForFrame(name = 'Test') {
|
|
112
163
|
try {
|
|
113
164
|
this.page.waitForFrame(async frame => {
|
|
114
|
-
return frame.name() ===
|
|
165
|
+
return await frame.name() === name;
|
|
115
166
|
});
|
|
116
167
|
return this;
|
|
117
168
|
}
|
|
@@ -127,7 +178,7 @@ module.exports = class {
|
|
|
127
178
|
async waitFrame(urlKey, timeout = 5000) {
|
|
128
179
|
try {
|
|
129
180
|
return await this.page.waitForFrame(async frame => {
|
|
130
|
-
return frame.url().
|
|
181
|
+
return frame.url().includes(urlKey);
|
|
131
182
|
}, { timeout });
|
|
132
183
|
}
|
|
133
184
|
catch (e) {
|
|
@@ -137,6 +188,7 @@ module.exports = class {
|
|
|
137
188
|
|
|
138
189
|
/**
|
|
139
190
|
* 按tag创建当前窗口中的某个iFrame
|
|
191
|
+
*
|
|
140
192
|
* @param {Object} tag
|
|
141
193
|
*/
|
|
142
194
|
async iframe(tag) {
|
|
@@ -245,6 +297,21 @@ module.exports = class {
|
|
|
245
297
|
}
|
|
246
298
|
}
|
|
247
299
|
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* 获取部分或全部HTML
|
|
303
|
+
* @param {Object} obj
|
|
304
|
+
*/
|
|
305
|
+
async evaluate(obj, callback) {
|
|
306
|
+
try {
|
|
307
|
+
return await this.page.evaluate(callback, obj);
|
|
308
|
+
}
|
|
309
|
+
catch (e) {
|
|
310
|
+
console.log('[chrome.evaluate.Error]', e.message);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
|
|
248
315
|
/**
|
|
249
316
|
* 获取部分或全部HTML
|
|
250
317
|
* @param {Object} obj
|
|
@@ -291,7 +358,7 @@ module.exports = class {
|
|
|
291
358
|
return this;
|
|
292
359
|
}
|
|
293
360
|
catch (e) {
|
|
294
|
-
console.log('[chrome.
|
|
361
|
+
console.log('[chrome.display.Error]', [tag, e.message]);
|
|
295
362
|
}
|
|
296
363
|
}
|
|
297
364
|
|
|
@@ -305,21 +372,19 @@ module.exports = class {
|
|
|
305
372
|
*/
|
|
306
373
|
async elements(tag1, tag2, call) {
|
|
307
374
|
try {
|
|
375
|
+
let has = await this.wait(tag, 2, 2);
|
|
376
|
+
if (!has) return null;
|
|
308
377
|
|
|
309
|
-
const div = await this.page.$(tag1);
|
|
310
|
-
if (!div) {
|
|
311
|
-
throw new Error(`${tag1} not exists`);
|
|
312
|
-
}
|
|
313
378
|
let index = 0;
|
|
314
|
-
for (const
|
|
315
|
-
const html = await
|
|
316
|
-
const text = await
|
|
317
|
-
call(
|
|
379
|
+
for (const ele of (await div.$$(tag2))) {
|
|
380
|
+
const html = await ele.evaluate(node => node.innerHTML);
|
|
381
|
+
const text = await ele.evaluate(node => node.innerText);
|
|
382
|
+
await call(ele, { html, text, index: index++ });
|
|
318
383
|
}
|
|
319
384
|
|
|
320
385
|
}
|
|
321
386
|
catch (e) {
|
|
322
|
-
console.log('[chrome.elements.Error]', e.message)
|
|
387
|
+
console.log('[chrome.elements.Error]', [tag1, tag2, e.message])
|
|
323
388
|
}
|
|
324
389
|
}
|
|
325
390
|
|
|
@@ -334,24 +399,23 @@ module.exports = class {
|
|
|
334
399
|
async search(tag1, tag2, call) {
|
|
335
400
|
return await new Promise(async (resolve) => {
|
|
336
401
|
try {
|
|
402
|
+
let has = await this.wait(tag, 2, 2);
|
|
403
|
+
if (!has) return {};
|
|
337
404
|
|
|
338
|
-
const div = await this.page.$(tag1);
|
|
339
|
-
if (!div) {
|
|
340
|
-
throw new Error(`${tag1} not exists`);
|
|
341
|
-
}
|
|
342
405
|
let index = 0;
|
|
343
406
|
for (const ele of (await div.$$(tag2))) {
|
|
344
407
|
index++;
|
|
345
|
-
const html = await ele.evaluate(node => node.
|
|
346
|
-
const text = await ele.evaluate(node => node.
|
|
347
|
-
let val = call(ele, { html, text, index });
|
|
408
|
+
const html = await ele.evaluate(node => node.innerHTML);
|
|
409
|
+
const text = await ele.evaluate(node => node.innerText);
|
|
410
|
+
let val = await call(ele, { html, text, index });
|
|
348
411
|
if (val === true) return resolve({ success: true, ele, html, text, index });
|
|
349
412
|
}
|
|
350
|
-
resolve({ success: false });
|
|
413
|
+
resolve({ success: false, index });
|
|
351
414
|
|
|
352
415
|
}
|
|
353
416
|
catch (e) {
|
|
354
|
-
console.log('[chrome.
|
|
417
|
+
console.log('[chrome.search.Error]', [tag1, tag2, e.message])
|
|
418
|
+
return {};
|
|
355
419
|
}
|
|
356
420
|
});
|
|
357
421
|
|
|
@@ -373,11 +437,14 @@ module.exports = class {
|
|
|
373
437
|
*/
|
|
374
438
|
async element(tag, selector = false) {
|
|
375
439
|
try {
|
|
440
|
+
let has = await this.wait(tag, 2, 2);
|
|
441
|
+
if (!has) return null;
|
|
442
|
+
|
|
376
443
|
if (selector) return await this.page.querySelector(tag);
|
|
377
444
|
return await this.page.$(tag);
|
|
378
445
|
}
|
|
379
446
|
catch (e) {
|
|
380
|
-
console.log('[chrome.element.Error]', e.message);
|
|
447
|
+
console.log('[chrome.element.Error]', [tag, e.message]);
|
|
381
448
|
return null;
|
|
382
449
|
}
|
|
383
450
|
}
|
|
@@ -437,9 +504,6 @@ module.exports = class {
|
|
|
437
504
|
}
|
|
438
505
|
}
|
|
439
506
|
|
|
440
|
-
/**
|
|
441
|
-
* delay=每键入一个字符延迟毫秒
|
|
442
|
-
*/
|
|
443
507
|
async clear(el) {
|
|
444
508
|
try {
|
|
445
509
|
await this.page.$eval(el, ele => ele.value = '');
|
|
@@ -480,12 +544,12 @@ module.exports = class {
|
|
|
480
544
|
*/
|
|
481
545
|
async click(el, option = {}) {
|
|
482
546
|
try {
|
|
547
|
+
|
|
483
548
|
let { delay = 100, count = 1, x = 6, y = 3 } = option;
|
|
484
549
|
return await this.page.click(el, { delay, count, offset: { x, y } });
|
|
485
|
-
// return this;
|
|
486
550
|
}
|
|
487
551
|
catch (e) {
|
|
488
|
-
console.log('[chrome.click.Error]', e.message);
|
|
552
|
+
console.log('[chrome.click.Error]', el, e.message);
|
|
489
553
|
}
|
|
490
554
|
}
|
|
491
555
|
|
|
@@ -640,59 +704,84 @@ module.exports = class {
|
|
|
640
704
|
return this;
|
|
641
705
|
}
|
|
642
706
|
|
|
643
|
-
delSkipType(
|
|
707
|
+
delSkipType(item) {
|
|
644
708
|
this.skipTypes = this.skipTypes.del(...item);
|
|
645
709
|
return this;
|
|
646
710
|
}
|
|
647
711
|
|
|
712
|
+
|
|
648
713
|
async parseResponse(response) {
|
|
649
714
|
// const response = await this.page.waitForResponse(res => res);
|
|
650
|
-
|
|
651
715
|
const value = {};
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
if (value.
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
716
|
+
|
|
717
|
+
try {
|
|
718
|
+
|
|
719
|
+
const request = await response.request();
|
|
720
|
+
const headers = await response.headers();
|
|
721
|
+
|
|
722
|
+
value.method = await request.method();
|
|
723
|
+
if (value.method === 'OPTIONS') return;
|
|
724
|
+
// if (value.method === 'DELETE') return;
|
|
725
|
+
value.index = this.resIndex++
|
|
726
|
+
|
|
727
|
+
value.type = await request.resourceType();
|
|
728
|
+
// value.redirect = await response.redirectURL();
|
|
729
|
+
if (value.type === 'fetch') value.type = 'xhr';
|
|
730
|
+
if (value.type === 'xhr') value.type = 'AJAX';
|
|
731
|
+
value.url = await response.url();
|
|
732
|
+
value.headers = { request: request.headers(), response: headers };
|
|
733
|
+
value.domain = (new URL(value.url))['host'];
|
|
734
|
+
value.content = headers['content-type'];
|
|
735
|
+
value.length = headers['content-length'];
|
|
736
|
+
value.status = await response.status();
|
|
737
|
+
value.ok = await response.ok();
|
|
738
|
+
value.datetime = (new Date(headers['date'])).date('yyyy-mm-dd hh:ii:ss');
|
|
739
|
+
if (headers['server']) value.server = headers['server'];
|
|
740
|
+
if (headers['set-cookie']) value.cookies = await this.cookies.parse(headers['set-cookie']);
|
|
741
|
+
value.remote = await response.remoteAddress(); //目标服务器
|
|
742
|
+
if (value.status === 301 || value.status === 302) return value;
|
|
743
|
+
if (this.skipTypes.includes(value.type)) return value;
|
|
744
|
+
|
|
745
|
+
value.post = await request.postData();
|
|
746
|
+
if (value.post) value.post = value.post.toString();
|
|
747
|
+
|
|
748
|
+
}
|
|
749
|
+
catch (e) {
|
|
750
|
+
const err = e.parse();
|
|
751
|
+
value.error = err.message;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
try {
|
|
755
|
+
|
|
756
|
+
if (value.content.includes('text/html')) {
|
|
757
|
+
const buffer = await response.buffer();
|
|
758
|
+
value.html = buffer.toString();
|
|
759
|
+
return value;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
if (value.content) {
|
|
763
|
+
if (value.content.includes('application/vnd')) return value;
|
|
764
|
+
if (value.content.includes('application/xml')) return value;
|
|
765
|
+
if (value.content.includes('application/javascript')) return value;
|
|
766
|
+
if (value.content.includes('text/css')) return value;
|
|
767
|
+
if (value.content.includes('text/plain')) return value;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
}
|
|
771
|
+
catch (e) {
|
|
772
|
+
const err = e.parse();
|
|
773
|
+
value.error = err.message;
|
|
774
|
+
}
|
|
687
775
|
|
|
688
776
|
try {
|
|
689
777
|
const buffer = await response.buffer();
|
|
690
778
|
value.response = buffer.toString();
|
|
779
|
+
if (!/^[\[\{].+[\]\}]$/.test(value.response)) return value;
|
|
691
780
|
value.json = JSON.parse(value.response);
|
|
692
781
|
}
|
|
693
782
|
catch (e) {
|
|
694
783
|
const err = e.parse();
|
|
695
|
-
value.
|
|
784
|
+
value.error = err.message;
|
|
696
785
|
}
|
|
697
786
|
|
|
698
787
|
return value;
|
|
@@ -707,6 +796,8 @@ module.exports = class {
|
|
|
707
796
|
async doListening(options) {
|
|
708
797
|
|
|
709
798
|
this.page.on('request', async (request) => {
|
|
799
|
+
if (!this.page.interception) return;
|
|
800
|
+
|
|
710
801
|
const host = this.host(request.url());
|
|
711
802
|
const headers = request.headers();
|
|
712
803
|
if (headers.cookie) this.cookies.request(headers.cookie, host);
|
|
@@ -714,6 +805,7 @@ module.exports = class {
|
|
|
714
805
|
if (this.requestCall) {
|
|
715
806
|
const run = await this.requestCall(request);
|
|
716
807
|
if (run === false) return request.abort();
|
|
808
|
+
if (run === true) return;
|
|
717
809
|
}
|
|
718
810
|
|
|
719
811
|
const rType = request.resourceType();
|