nodejs_chromium 1.1.13 → 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.
Files changed (3) hide show
  1. package/index.js +14 -5
  2. package/package.json +1 -1
  3. package/src/chrome.js +192 -62
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)) return;
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) await page.setUserAgent(mob_ua);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodejs_chromium",
3
- "version": "1.1.13",
3
+ "version": "1.1.16",
4
4
  "description": "for pupeteer chromium",
5
5
  "main": "index.js",
6
6
  "scripts": {
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
- request(call) {
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() === 'Test';
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().indexOf(urlKey) > 0;
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.text.Error]', e.message);
361
+ console.log('[chrome.display.Error]', [tag, e.message]);
295
362
  }
296
363
  }
297
364
 
@@ -305,22 +372,56 @@ module.exports = class {
305
372
  */
306
373
  async elements(tag1, tag2, call) {
307
374
  try {
308
- const div = await this.page.$(tag1);
309
- if (!div) {
310
- throw new Error(`${tag1} not exists`);
311
- }
312
- for (const elm of (await div.$$(tag2))) {
313
- const html = await elm.evaluate(node => node.outerHTML);
314
- const text = await elm.evaluate(node => node.innerHTML);
315
- call(elm, { html, text });
375
+ let has = await this.wait(tag, 2, 2);
376
+ if (!has) return null;
377
+
378
+ let index = 0;
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++ });
316
383
  }
384
+
317
385
  }
318
386
  catch (e) {
319
- console.log('[chrome.elements.Error]', e.message)
387
+ console.log('[chrome.elements.Error]', [tag1, tag2, e.message])
320
388
  }
321
389
  }
322
390
 
323
391
 
392
+ /**
393
+ * 遍历tag1里的tag2,并由call判断,只有call返回true时才resolve
394
+ *
395
+ * @param {Object} tag1
396
+ * @param {Object} tag2
397
+ * @param {Object} call
398
+ */
399
+ async search(tag1, tag2, call) {
400
+ return await new Promise(async (resolve) => {
401
+ try {
402
+ let has = await this.wait(tag, 2, 2);
403
+ if (!has) return {};
404
+
405
+ let index = 0;
406
+ for (const ele of (await div.$$(tag2))) {
407
+ 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 });
411
+ if (val === true) return resolve({ success: true, ele, html, text, index });
412
+ }
413
+ resolve({ success: false, index });
414
+
415
+ }
416
+ catch (e) {
417
+ console.log('[chrome.search.Error]', [tag1, tag2, e.message])
418
+ return {};
419
+ }
420
+ });
421
+
422
+ }
423
+
424
+
324
425
  /**
325
426
  * 支持css普通选择器方式和伪类方式
326
427
  * div.body
@@ -336,11 +437,14 @@ module.exports = class {
336
437
  */
337
438
  async element(tag, selector = false) {
338
439
  try {
440
+ let has = await this.wait(tag, 2, 2);
441
+ if (!has) return null;
442
+
339
443
  if (selector) return await this.page.querySelector(tag);
340
444
  return await this.page.$(tag);
341
445
  }
342
446
  catch (e) {
343
- console.log('[chrome.element.Error]', e.message);
447
+ console.log('[chrome.element.Error]', [tag, e.message]);
344
448
  return null;
345
449
  }
346
450
  }
@@ -400,9 +504,6 @@ module.exports = class {
400
504
  }
401
505
  }
402
506
 
403
- /**
404
- * delay=每键入一个字符延迟毫秒
405
- */
406
507
  async clear(el) {
407
508
  try {
408
509
  await this.page.$eval(el, ele => ele.value = '');
@@ -443,12 +544,12 @@ module.exports = class {
443
544
  */
444
545
  async click(el, option = {}) {
445
546
  try {
547
+
446
548
  let { delay = 100, count = 1, x = 6, y = 3 } = option;
447
- await this.page.click(el, { delay, count, offset: { x, y } });
448
- return this;
549
+ return await this.page.click(el, { delay, count, offset: { x, y } });
449
550
  }
450
551
  catch (e) {
451
- console.log('[chrome.click.Error]', e.message);
552
+ console.log('[chrome.click.Error]', el, e.message);
452
553
  }
453
554
  }
454
555
 
@@ -596,65 +697,91 @@ module.exports = class {
596
697
  }
597
698
 
598
699
  skipTypes = ['image', 'font', 'other', 'script', 'stylesheet', 'document', 'ping'];
700
+
599
701
  setSkipType(items) {
600
702
  this.skipTypes.length = 0;
601
703
  this.skipTypes = [...items];
602
704
  return this;
603
705
  }
604
706
 
605
- delSkipType(...item) {
707
+ delSkipType(item) {
606
708
  this.skipTypes = this.skipTypes.del(...item);
607
709
  return this;
608
710
  }
609
711
 
712
+
610
713
  async parseResponse(response) {
611
714
  // const response = await this.page.waitForResponse(res => res);
612
-
613
715
  const value = {};
614
- const request = await response.request();
615
- const headers = await response.headers();
616
- value.method = await request.method();
617
- if (value.method === 'OPTIONS') return;
618
-
619
- value.type = await request.resourceType();
620
- // value.redirect = await response.redirectURL();
621
- if (value.type === 'fetch') value.type = 'xhr';
622
- if (value.type === 'xhr') value.type = 'AJAX';
623
- value.url = await response.url();
624
- value.headers = { request: request.headers(), response: headers };
625
- value.domain = (new URL(value.url))['host'];
626
- value.content = headers['content-type'];
627
- value.length = headers['content-length'];
628
- value.status = await response.status();
629
- value.ok = await response.ok();
630
- value.datetime = (new Date(headers['date'])).date('yyyy-mm-dd hh:ii:ss');
631
- // value.headers = headers;
632
- if (headers['server']) value.server = headers['server'];
633
- if (headers['set-cookie']) value.cookies = await this.cookies.parse(headers['set-cookie']);
634
- value.remote = await response.remoteAddress(); //目标服务器
635
- if (value.status === 301 || value.status === 302) return value;
636
-
637
- // if (['image', 'font', 'other', 'script', 'stylesheet', 'document', 'ping'].has(value.type)) return value;
638
- // if (['image', 'font', 'other', 'script', 'stylesheet', 'ping'].includes(value.type)) return value;
639
- if (this.skipTypes.includes(value.type)) return value;
640
-
641
- if (value.content) {
642
- if (value.content.startsWith('application/vnd')) return value;
643
- if (value.content.startsWith('application/xml')) return value;
644
- if (value.content.startsWith('text/css')) return value;
645
- }
646
-
647
- value.post = await request.postData();
648
- if (value.post) value.post = value.post.toString();
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
+ }
649
775
 
650
776
  try {
651
777
  const buffer = await response.buffer();
652
778
  value.response = buffer.toString();
779
+ if (!/^[\[\{].+[\]\}]$/.test(value.response)) return value;
653
780
  value.json = JSON.parse(value.response);
654
781
  }
655
782
  catch (e) {
656
783
  const err = e.parse();
657
- value.json = err.message;
784
+ value.error = err.message;
658
785
  }
659
786
 
660
787
  return value;
@@ -669,6 +796,8 @@ module.exports = class {
669
796
  async doListening(options) {
670
797
 
671
798
  this.page.on('request', async (request) => {
799
+ if (!this.page.interception) return;
800
+
672
801
  const host = this.host(request.url());
673
802
  const headers = request.headers();
674
803
  if (headers.cookie) this.cookies.request(headers.cookie, host);
@@ -676,6 +805,7 @@ module.exports = class {
676
805
  if (this.requestCall) {
677
806
  const run = await this.requestCall(request);
678
807
  if (run === false) return request.abort();
808
+ if (run === true) return;
679
809
  }
680
810
 
681
811
  const rType = request.resourceType();