nodejs_chromium 1.0.7 → 1.1.0
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 +39 -27
- package/package.json +1 -1
- package/src/chrome.js +53 -167
- package/src/cookies.js +179 -0
package/index.js
CHANGED
|
@@ -6,56 +6,66 @@ global.__UA__ = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (K
|
|
|
6
6
|
async function newChrome(params) {
|
|
7
7
|
let {
|
|
8
8
|
id = 'myChrome',
|
|
9
|
-
visible = false,
|
|
10
|
-
width = 1024,
|
|
11
|
-
height = 768,
|
|
12
|
-
scale = 1,
|
|
13
|
-
mobile = false,
|
|
14
|
-
dumpio = false,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
9
|
+
visible = false, //是否打开浏览器
|
|
10
|
+
width = 1024, //宽
|
|
11
|
+
height = 768, //高
|
|
12
|
+
scale = 1, //缩放比例
|
|
13
|
+
mobile = false, //手机版
|
|
14
|
+
dumpio = false, //转发标准输入/输出流
|
|
15
|
+
devtools = false, //打开devtools
|
|
16
|
+
cache = false, //禁用缓存
|
|
17
|
+
incognito = true, //使用无痕模式启动
|
|
18
|
+
path = void 0, //Chrome路径,linux下必填
|
|
19
|
+
slowMo = 1, //每一步停留时间
|
|
20
|
+
ua = void 0, //若不指定ua则用__UA__
|
|
21
|
+
proxy = null, //代理,如:https://proxy.com:789/
|
|
22
|
+
cookies = null, //若cookies=false,则不处理cookies,不指定则由chrome处理,若=文件路径
|
|
23
|
+
abort = [], //要禁止的类型,常见["font","image","ping","stylesheet","document","fetch","script","xhr"]
|
|
24
|
+
headers = {},
|
|
20
25
|
} = params;
|
|
21
26
|
|
|
22
27
|
let option = {
|
|
23
|
-
userDataDir: `runtime
|
|
28
|
+
userDataDir: `runtime/.cache/${id}`,
|
|
24
29
|
// timeout: 500,//最大允许超时间,默认为3000,最小215,一般不要设置
|
|
25
30
|
slowMo, //每一步停留时间,不能太大,否则会太慢,特别是在类似写入很多Cookies时,每写入一个都要等一下。
|
|
26
31
|
headless: visible ? false : 'new', //'new',设置是否在无头模式下运行浏览器,false=会启动浏览器,true=无界面
|
|
27
|
-
devtools: !!
|
|
32
|
+
devtools: !!devtools, //打开调试
|
|
28
33
|
ignoreHTTPSErrors: true, //忽略 HTTPS 错误。屏蔽跳转不同域名的报错
|
|
29
|
-
|
|
30
|
-
"--enable-automation", //忽略默认的 --enable-automation 参数,这有助于防止某些网站检测到自动化行为。
|
|
31
|
-
],
|
|
32
|
-
dumpio: !!dumpio, //是否将浏览器的标准输入/输出流(stdio)的内容输出到 Node.js 的 stdout 和 stderr。
|
|
34
|
+
dumpio, //是否将浏览器的标准输入/输出流(stdio)的内容输出到 Node.js 的 stdout 和 stderr。
|
|
33
35
|
defaultViewport: {
|
|
34
36
|
width,
|
|
35
37
|
height,
|
|
36
38
|
deviceScaleFactor: scale, //缩放比例
|
|
37
39
|
isMobile: !!mobile,
|
|
38
40
|
},
|
|
41
|
+
defaultArgs: [
|
|
42
|
+
'--disable-extensions', //禁止启动扩展
|
|
43
|
+
'--no-sandbox', //禁用沙箱模式
|
|
44
|
+
'--disable-setuid-sandbox', //禁用设置用户身份沙盒
|
|
45
|
+
'--disable-web-security', //禁用同源策略
|
|
46
|
+
],
|
|
47
|
+
ignoreDefaultArgs: [
|
|
48
|
+
"--enable-automation", //忽略默认的 --enable-automation 参数,这有助于防止某些网站检测到自动化行为。
|
|
49
|
+
],
|
|
39
50
|
args: [
|
|
40
51
|
'--enable-chrome-browser-cloud-management', //Cloud Browser Client Management (CBCM)
|
|
41
52
|
'--disable-web-security', //禁用浏览器的同源策略(Same-Origin Policy)和跨站请求伪造(CSRF)保护
|
|
42
53
|
`--window-size=${width},${height}`,
|
|
43
54
|
'--no-sandbox', //禁用沙箱模式
|
|
55
|
+
'--disable-autofill-backend', //禁止自动填充
|
|
44
56
|
'--disable-setuid-sandbox', //禁用 setuid 沙箱。这是另一种沙箱模式,通常用于 Linux 系统上。
|
|
45
|
-
'--disable-infobars',
|
|
57
|
+
// '--disable-infobars', //禁用自动化控制时显示的信息栏,告诉用户浏览器正在被自动化工具控制。
|
|
46
58
|
'--disable-gpu', //禁用 GPU 加速。这通常用于在服务器环境或某些不支持 GPU 加速的平台上运行 Chrome。
|
|
47
|
-
'--disable-blink-features=AutomationControlled',
|
|
48
|
-
// 在自动化测试或爬虫等场景中,这个参数可以帮助隐藏浏览器的自动化痕迹,使得浏览器行为更接近于真实用户操作
|
|
59
|
+
'--disable-blink-features=AutomationControlled', //防止检测 blink 引擎特性判断是否处于自动化控制状态。
|
|
49
60
|
'--lang=zh-CN', //设置中文环境
|
|
50
61
|
'--disable-extensions', //禁止启动扩展
|
|
51
62
|
'--disable-dev-shm-usage', //Linux系统中使用普通的文件系统缓存避免因为/dev/shm大小不足而导致的问题
|
|
52
63
|
]
|
|
53
64
|
}
|
|
54
65
|
|
|
55
|
-
if (
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
66
|
+
if (proxy) option.defaultArgs.push(`--proxy-server=${proxy}`);
|
|
67
|
+
if (incognito) option.args.push('--incognito', '--disable-infobars'); //使用无痕模式启动
|
|
68
|
+
if (!cache) option.args.push('--disable-cache'); //禁缓存
|
|
59
69
|
if (path) option.executablePath = path; //指定chrome安装路径
|
|
60
70
|
|
|
61
71
|
const browser = await puppeteer.launch(option);
|
|
@@ -65,11 +75,13 @@ async function newChrome(params) {
|
|
|
65
75
|
if (ua) await page.setUserAgent(ua);
|
|
66
76
|
await page.evaluateOnNewDocument(() => {
|
|
67
77
|
const newProto = navigator.__proto__;
|
|
68
|
-
delete newProto.webdriver; //删除 navigator.webdriver
|
|
78
|
+
delete newProto.webdriver; //删除 navigator.webdriver字段,防止检测到自动化行为
|
|
69
79
|
navigator.__proto__ = newProto;
|
|
70
|
-
});
|
|
80
|
+
});
|
|
71
81
|
|
|
72
|
-
|
|
82
|
+
if (cookies !== false) cookies = `runtime/.cache/${id}/cookies.json`;
|
|
83
|
+
const pageOption = { cookies, visible, abort, headers };
|
|
84
|
+
return new chrome(browser, page, pageOption);
|
|
73
85
|
}
|
|
74
86
|
|
|
75
87
|
|
package/package.json
CHANGED
package/src/chrome.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const fs = require("fs");
|
|
2
|
+
const Cookies = require("./cookies");
|
|
2
3
|
const { parse: parseUrl } = require("url");
|
|
3
4
|
|
|
4
5
|
|
|
@@ -12,21 +13,23 @@ const { parse: parseUrl } = require("url");
|
|
|
12
13
|
* @type {exports}
|
|
13
14
|
*/
|
|
14
15
|
module.exports = class {
|
|
15
|
-
browser =
|
|
16
|
-
page =
|
|
17
|
-
responseCall =
|
|
18
|
-
requestCall =
|
|
16
|
+
browser = void 0;
|
|
17
|
+
page = void 0;
|
|
18
|
+
responseCall = void 0;
|
|
19
|
+
requestCall = void 0;
|
|
20
|
+
cookies = void 0;
|
|
21
|
+
options = void 0;
|
|
19
22
|
isFrame = false; //是不是在iFrame中
|
|
20
23
|
visible = false; //是否可见,也就是有没有启动窗口
|
|
21
|
-
params = {};
|
|
22
24
|
|
|
23
|
-
constructor(browser, page,
|
|
25
|
+
constructor(browser, page, options, isFrame = false) {
|
|
24
26
|
this.browser = browser;
|
|
25
27
|
this.page = page;
|
|
26
|
-
this.
|
|
27
|
-
this.
|
|
28
|
+
this.options = options;
|
|
29
|
+
this.cookies = new Cookies(page, options.cookies);
|
|
30
|
+
this.visible = !!options.visible;
|
|
28
31
|
this.isFrame = !!isFrame;
|
|
29
|
-
this.doListening(
|
|
32
|
+
this.doListening(options);
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
request(call) {
|
|
@@ -45,14 +48,14 @@ module.exports = class {
|
|
|
45
48
|
async clone() {
|
|
46
49
|
try {
|
|
47
50
|
const page = await this.browser.newPage();
|
|
51
|
+
await page.setBypassCSP(true); //绕过页面的内容安全策略
|
|
48
52
|
await page.setRequestInterception(true); //允许拦截
|
|
49
|
-
// await page.setUserAgent(this.params.ua);
|
|
50
53
|
await page.evaluateOnNewDocument(() => {
|
|
51
54
|
const newProto = navigator.__proto__;
|
|
52
55
|
delete newProto.webdriver; //删除 navigator.webdriver字段
|
|
53
56
|
navigator.__proto__ = newProto; //在每次新文档加载时,删除 navigator.webdriver 字段,这有助于防止某些网站检测到自动化行为。
|
|
54
57
|
});
|
|
55
|
-
return new module.exports(this.browser, page, this.
|
|
58
|
+
return new module.exports(this.browser, page, this.options, false); //new 自身
|
|
56
59
|
}
|
|
57
60
|
catch (e) {
|
|
58
61
|
console.log('[chrome.iframe.Error]', e.message);
|
|
@@ -139,8 +142,7 @@ module.exports = class {
|
|
|
139
142
|
async iframe(tag) {
|
|
140
143
|
try {
|
|
141
144
|
const frame = await (await this.page.$(tag)).contentFrame();
|
|
142
|
-
|
|
143
|
-
return new module.exports(this.browser, frame, this.params, true); //new 自身
|
|
145
|
+
return new module.exports(this.browser, frame, this.options, true); //new 自身
|
|
144
146
|
}
|
|
145
147
|
catch (e) {
|
|
146
148
|
console.log('[chrome.iframe.Error]', e.message);
|
|
@@ -150,8 +152,7 @@ module.exports = class {
|
|
|
150
152
|
/**
|
|
151
153
|
* 关闭
|
|
152
154
|
*/
|
|
153
|
-
async close(act =
|
|
154
|
-
|
|
155
|
+
async close(act = 0) {
|
|
155
156
|
try {
|
|
156
157
|
if (act & 1) await this.page.close();
|
|
157
158
|
if (act & 2) await this.browser.close();
|
|
@@ -327,10 +328,14 @@ module.exports = class {
|
|
|
327
328
|
* iframe:first-child
|
|
328
329
|
* div>ul>li:nth-child(4)>a
|
|
329
330
|
*
|
|
331
|
+
* querySelector,若匹配不到返回null
|
|
332
|
+
* page.$(),若匹配不上则会抛出错误,这里最后也会在cath中返回null
|
|
333
|
+
*
|
|
330
334
|
* @param {Object} tag
|
|
331
335
|
*/
|
|
332
|
-
async element(tag) {
|
|
336
|
+
async element(tag, selector = false) {
|
|
333
337
|
try {
|
|
338
|
+
if (selector) return await this.page.querySelector(tag);
|
|
334
339
|
return await this.page.$(tag);
|
|
335
340
|
}
|
|
336
341
|
catch (e) {
|
|
@@ -382,6 +387,7 @@ module.exports = class {
|
|
|
382
387
|
async waiting(time) {
|
|
383
388
|
return await this.sleep(time);
|
|
384
389
|
}
|
|
390
|
+
|
|
385
391
|
async sleep(time) {
|
|
386
392
|
if (time < 100) time = time * 1000;
|
|
387
393
|
try {
|
|
@@ -495,90 +501,6 @@ module.exports = class {
|
|
|
495
501
|
}
|
|
496
502
|
|
|
497
503
|
|
|
498
|
-
jsonArray(cookiesVal, host) {
|
|
499
|
-
if (!cookiesVal) return [];
|
|
500
|
-
return JSON.parse(cookiesVal).map(cook => {
|
|
501
|
-
let { name, value, domain } = cook;
|
|
502
|
-
if (!domain) domain = host;
|
|
503
|
-
return { name, value, domain };
|
|
504
|
-
});
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
/**
|
|
508
|
-
|
|
509
|
-
// const url = parseUrl(this.page.url());
|
|
510
|
-
// const host = '.' + url.host.split('.').slice(-2).join('.');
|
|
511
|
-
// let cookiesVal = read(file, 'utf8');
|
|
512
|
-
// cookiesVal = this.jsonArray(cookiesVal, host);
|
|
513
|
-
|
|
514
|
-
*/
|
|
515
|
-
|
|
516
|
-
/**
|
|
517
|
-
* 合并两个Cookies,并以后面的值为准
|
|
518
|
-
*
|
|
519
|
-
* @param {Object} oldCookies
|
|
520
|
-
* @param {Object} newCookies
|
|
521
|
-
*/
|
|
522
|
-
async mergeNewCookies(oldCookies, newCookies) {
|
|
523
|
-
if (newCookies.length === 0) return oldCookies;
|
|
524
|
-
// let aMap = new Map(oldCookies.map(obj => [obj.name, obj]));//浅拷贝
|
|
525
|
-
let aMap = JSON.parse(JSON.stringify(this.cookies)); //深拷贝
|
|
526
|
-
newCookies.map(obj => {
|
|
527
|
-
aMap.set(obj.name, obj)
|
|
528
|
-
});
|
|
529
|
-
return await Array.from(aMap.values());
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
/**
|
|
534
|
-
* 获取当前页面的Cookies
|
|
535
|
-
*/
|
|
536
|
-
async getCookies() {
|
|
537
|
-
try {
|
|
538
|
-
return await this.page.cookies();
|
|
539
|
-
}
|
|
540
|
-
catch (e) {
|
|
541
|
-
console.log('[chrome.getCookies.Error]', e.message);
|
|
542
|
-
return [];
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
/**
|
|
547
|
-
* 设置Cookies
|
|
548
|
-
* @param {Object} cookies
|
|
549
|
-
*/
|
|
550
|
-
async setCookies(cookies) {
|
|
551
|
-
try {
|
|
552
|
-
await this.page.setCookie(...cookies);
|
|
553
|
-
return this;
|
|
554
|
-
}
|
|
555
|
-
catch (e) {
|
|
556
|
-
console.log('[chrome.setCookies.Error]', e.message);
|
|
557
|
-
console.log(cookies);
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
/**
|
|
562
|
-
* 保存当前页面中的Cookies
|
|
563
|
-
* @param {Object} file
|
|
564
|
-
*/
|
|
565
|
-
async saveCookies(file, append = true) {
|
|
566
|
-
try {
|
|
567
|
-
let cookies = await this.page.cookies();
|
|
568
|
-
|
|
569
|
-
if (append && fs.existsSync(file)) {
|
|
570
|
-
let dbCookies = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
571
|
-
cookies = this.mergeNewCookies(dbCookies, cookies);
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
await fs.writeFileSync(file, JSON.stringify(cookies, null, 2));
|
|
575
|
-
return this;
|
|
576
|
-
}
|
|
577
|
-
catch (e) {
|
|
578
|
-
console.log('[chrome.saveCookies.Error]', e.message);
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
|
|
582
504
|
/**
|
|
583
505
|
* 加水印,这里实际上是在网页加个DIV并显示时间
|
|
584
506
|
* @param {Object} conf
|
|
@@ -633,20 +555,20 @@ module.exports = class {
|
|
|
633
555
|
}
|
|
634
556
|
|
|
635
557
|
/**
|
|
636
|
-
* 补全所有本地js/css
|
|
558
|
+
* 补全所有本地js/css,一般用于保存html之前
|
|
637
559
|
*/
|
|
638
560
|
async improveUrls() {
|
|
639
561
|
const url = parseUrl(this.page.url());
|
|
640
562
|
const domain = url.protocol + '//' + url.host;
|
|
641
563
|
await this.page.evaluate((domain) => {
|
|
642
564
|
try {
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
565
|
+
document.querySelectorAll('script[src], link[href], iframe[src]')
|
|
566
|
+
.forEach((ele) => {
|
|
567
|
+
const src = ele.getAttribute('src');
|
|
568
|
+
const href = ele.getAttribute('href');
|
|
569
|
+
if (src && src.startsWith('/')) ele.src = domain + src;
|
|
570
|
+
if (href && href.startsWith('/')) ele.href = domain + href;
|
|
571
|
+
});
|
|
650
572
|
}
|
|
651
573
|
catch (e) {
|
|
652
574
|
console.log('[chrome.improveUrls.Error]', e.message);
|
|
@@ -655,40 +577,6 @@ module.exports = class {
|
|
|
655
577
|
}
|
|
656
578
|
|
|
657
579
|
|
|
658
|
-
/**
|
|
659
|
-
* 解析网页set-cookies的值
|
|
660
|
-
*
|
|
661
|
-
* @param {Object} strCookies
|
|
662
|
-
*/
|
|
663
|
-
async parseCookies(strCookies) {
|
|
664
|
-
return await strCookies.split("\n").map((ls) => {
|
|
665
|
-
let value = {};
|
|
666
|
-
ls.split(';').map((ln, j) => {
|
|
667
|
-
// console.log(ln);
|
|
668
|
-
const arr = ln.split('=');
|
|
669
|
-
const Key = (arr[0]).trim();
|
|
670
|
-
if (!Key) return;
|
|
671
|
-
|
|
672
|
-
if (j === 0) {
|
|
673
|
-
value.name = Key;
|
|
674
|
-
value.value = arr[1];
|
|
675
|
-
}
|
|
676
|
-
else if (Key === 'Max-Age') {
|
|
677
|
-
value.expire = parseInt(arr[1]) + (Date.now() / 1000);
|
|
678
|
-
}
|
|
679
|
-
else if (Key === 'Secure') {
|
|
680
|
-
value.source = true;
|
|
681
|
-
value.sourceScheme = 'Secure';
|
|
682
|
-
}
|
|
683
|
-
else {
|
|
684
|
-
value[Key[0].toLowerCase() + Key.substring(1)] = arr[1] || true;
|
|
685
|
-
}
|
|
686
|
-
})
|
|
687
|
-
return value;
|
|
688
|
-
})
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
|
|
692
580
|
async parseResponse(response) {
|
|
693
581
|
// const response = await this.page.waitForResponse(res => res);
|
|
694
582
|
|
|
@@ -710,7 +598,7 @@ module.exports = class {
|
|
|
710
598
|
value.datetime = (new Date(headers['date'])).date('yyyy-mm-dd hh:ii:ss');
|
|
711
599
|
// value.headers = headers;
|
|
712
600
|
if (headers['server']) value.server = headers['server'];
|
|
713
|
-
if (headers['set-cookie']) value.cookies = await this.
|
|
601
|
+
if (headers['set-cookie']) value.cookies = await this.cookies.parse(headers['set-cookie']);
|
|
714
602
|
value.remote = await response.remoteAddress(); //目标服务器
|
|
715
603
|
if (value.status === 301 || value.status === 302) return value;
|
|
716
604
|
if (['image', 'font', 'other', 'script', 'stylesheet', 'document', 'ping', 'fetch'].has(value.type)) return value;
|
|
@@ -724,49 +612,47 @@ module.exports = class {
|
|
|
724
612
|
if (value.post) value.post = value.post.toString();
|
|
725
613
|
|
|
726
614
|
try {
|
|
727
|
-
|
|
728
|
-
value.response =
|
|
615
|
+
const buffer = await response.buffer();
|
|
616
|
+
value.response = buffer.toString();
|
|
729
617
|
value.json = JSON.parse(value.response);
|
|
730
618
|
}
|
|
731
619
|
catch (e) {
|
|
732
|
-
|
|
620
|
+
const err = e.parse();
|
|
621
|
+
value.json = err.message;
|
|
733
622
|
}
|
|
734
623
|
|
|
735
624
|
return value;
|
|
736
625
|
}
|
|
737
626
|
|
|
627
|
+
host(url) {
|
|
628
|
+
// const domain = url.protocol + '//' + url.host;
|
|
629
|
+
const urls = parseUrl(url);
|
|
630
|
+
return '.' + urls.host.split('.').slice(-2).join('.');
|
|
631
|
+
}
|
|
738
632
|
|
|
739
|
-
|
|
633
|
+
|
|
634
|
+
async doListening(options) {
|
|
740
635
|
|
|
741
636
|
this.page.on('request', async (request) => {
|
|
637
|
+
const host = this.host(request.url());
|
|
638
|
+
const headers = request.headers();
|
|
639
|
+
if (headers.cookie) this.cookies.request(headers.cookie, host);
|
|
640
|
+
|
|
742
641
|
if (this.requestCall) {
|
|
743
642
|
const run = await this.requestCall(request);
|
|
744
|
-
if (run === false)
|
|
745
|
-
request.abort();
|
|
746
|
-
return;
|
|
747
|
-
}
|
|
643
|
+
if (run === false) return request.abort();
|
|
748
644
|
}
|
|
749
645
|
|
|
750
|
-
const
|
|
751
|
-
|
|
752
|
-
request.abort();
|
|
753
|
-
return;
|
|
754
|
-
}
|
|
646
|
+
const rType = request.resourceType();
|
|
647
|
+
const { abort = [], headers: optHead = {} } = options;
|
|
755
648
|
|
|
756
|
-
|
|
757
|
-
|
|
649
|
+
if (abort.length > 0 && abort.some(t => t === rType)) return request.abort();
|
|
650
|
+
|
|
651
|
+
if (optHead !== {}) Object.assign(headers, optHead);
|
|
758
652
|
|
|
759
653
|
headers['Access-Control-Allow-Origin'] = '*'; // 设置允许跨源访问的域名,可以根据需求修改
|
|
760
654
|
headers['Access-Control-Allow-Methods'] = '*'; //'GET, POST, PUT, OPTIONS';
|
|
761
655
|
// headers['Access-Control-Allow-Headers'] = 'Content-Type';
|
|
762
|
-
|
|
763
|
-
if (no_cache) {
|
|
764
|
-
const rType = request.resourceType();
|
|
765
|
-
if (rType === 'script' || rType === 'stylesheet') {
|
|
766
|
-
headers['Cache-Control'] = 'no-store'; // 禁用缓存
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
|
|
770
656
|
request.continue({ headers });
|
|
771
657
|
});
|
|
772
658
|
|
|
@@ -775,7 +661,7 @@ module.exports = class {
|
|
|
775
661
|
let json = await this.parseResponse(res);
|
|
776
662
|
await this.responseCall(json);
|
|
777
663
|
});
|
|
778
|
-
}
|
|
779
664
|
|
|
665
|
+
}
|
|
780
666
|
|
|
781
667
|
}
|
package/src/cookies.js
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const {parse: parseUrl} = require("url");
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
module.exports = class {
|
|
6
|
+
page = void 0;
|
|
7
|
+
file = void 0;
|
|
8
|
+
cookies = [];
|
|
9
|
+
|
|
10
|
+
constructor(page, cookies) {
|
|
11
|
+
this.page = page;
|
|
12
|
+
if (cookies === false) return;
|
|
13
|
+
|
|
14
|
+
this.file = cookies;
|
|
15
|
+
if (!fs.existsSync(cookies)) return;
|
|
16
|
+
// console.log('cookies file:', cookies);
|
|
17
|
+
let cookiesVal = fs.readFileSync(cookies, 'utf8');
|
|
18
|
+
if (cookiesVal === '{}') cookiesVal = '[]';
|
|
19
|
+
this.cookies = JSON.parse(cookiesVal);
|
|
20
|
+
// this.set(this.cookies);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
json(str, host) {
|
|
24
|
+
if (!str) return [];
|
|
25
|
+
return JSON.parse(str).map(cook => {
|
|
26
|
+
let {name, value, domain} = cook;
|
|
27
|
+
if (!domain) domain = host;
|
|
28
|
+
return {name, value, domain};
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
|
|
34
|
+
// const url = parseUrl(this.page.url());
|
|
35
|
+
// const host = '.' + url.host.split('.').slice(-2).join('.');
|
|
36
|
+
// let cookiesVal = read(file, 'utf8');
|
|
37
|
+
// cookiesVal = this.jsonArray(cookiesVal, host);
|
|
38
|
+
|
|
39
|
+
*/
|
|
40
|
+
/**
|
|
41
|
+
* 解析网页set-cookies的值
|
|
42
|
+
*
|
|
43
|
+
* @param {Object} strCookies
|
|
44
|
+
*/
|
|
45
|
+
async parse(strCookies) {
|
|
46
|
+
return await strCookies.split("\n").map((ls) => {
|
|
47
|
+
let value = {};
|
|
48
|
+
ls.split(';').map((ln, j) => {
|
|
49
|
+
// console.log(ln);
|
|
50
|
+
const arr = ln.split('=');
|
|
51
|
+
const Key = (arr[0]).trim();
|
|
52
|
+
if (!Key) return;
|
|
53
|
+
|
|
54
|
+
if (j === 0) {
|
|
55
|
+
value.name = Key;
|
|
56
|
+
value.value = arr[1];
|
|
57
|
+
} else if (Key === 'Max-Age') {
|
|
58
|
+
value.expire = parseInt(arr[1]) + (Date.now() / 1000);
|
|
59
|
+
if (value.expire > 0) value.expire_date = (value.expire * 1000).date('Y-m-d H:i:s.SSS')
|
|
60
|
+
//有可能存在expires
|
|
61
|
+
} else if (Key === 'Secure') {
|
|
62
|
+
value.source = true;
|
|
63
|
+
value.sourceScheme = 'Secure';
|
|
64
|
+
} else {
|
|
65
|
+
value[Key[0].toLowerCase() + Key.substring(1)] = arr[1] || true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (value.expires && value.expires > 0) {
|
|
69
|
+
value.expires_date = (value.expires * 1000).date('YYYY-mm-dd HH:ii:ss.SSS')
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
})
|
|
73
|
+
return value;
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 获取当前页面的Cookies
|
|
80
|
+
*/
|
|
81
|
+
async get(key) {
|
|
82
|
+
try {
|
|
83
|
+
await this.merge();
|
|
84
|
+
// const cookies = await this.page.cookies();
|
|
85
|
+
if (key === undefined) return this.cookies;
|
|
86
|
+
return (this.cookies.filter(c => c.name === key) || [{}])[0];
|
|
87
|
+
} catch (e) {
|
|
88
|
+
console.log('[chrome.cookies.get.Error]', e.message);
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* 设置Cookies
|
|
95
|
+
* @param {Object} cookies
|
|
96
|
+
*/
|
|
97
|
+
async set(cookies) {
|
|
98
|
+
try {
|
|
99
|
+
if (cookies === undefined) cookies = this.cookies;
|
|
100
|
+
await this.page.setCookie(...cookies.map(ck => {
|
|
101
|
+
if (typeof ck.expires === 'string') ck.expires = new Date(ck.expires).getTime();
|
|
102
|
+
return ck;
|
|
103
|
+
}).filter(ck => (!!ck.domain || !!ck.url)));
|
|
104
|
+
} catch (e) {
|
|
105
|
+
console.log('[chrome.cookies.set.Error]', e.message);
|
|
106
|
+
console.log(JSON.stringify(cookies));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* 保存当前页面中的Cookies
|
|
112
|
+
*/
|
|
113
|
+
async save() {
|
|
114
|
+
try {
|
|
115
|
+
if (!this.file) return;
|
|
116
|
+
await this.merge();
|
|
117
|
+
await fs.writeFileSync(this.file, JSON.stringify(this.cookies, null, 2));
|
|
118
|
+
} catch (e) {
|
|
119
|
+
console.log('[chrome.cookies.save.Error]', e.message);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async merge() {
|
|
124
|
+
try {
|
|
125
|
+
let cookies = await this.page.cookies();
|
|
126
|
+
// console.log('merge>>>>', cookies, '<<<<<merge');
|
|
127
|
+
if (cookies.length === 0) return;
|
|
128
|
+
|
|
129
|
+
const newMap = new Map(this.cookies.map(obj => [obj.name, obj]));
|
|
130
|
+
cookies.map(obj => {
|
|
131
|
+
if (obj.expires && obj.expires > 0) obj.expires_date = (obj.expires * 1000).date('yyyy-mm-dd HH:ii:ss.SSS')
|
|
132
|
+
newMap.set(obj.name, obj)
|
|
133
|
+
});
|
|
134
|
+
this.cookies = await Array.from(newMap.values());
|
|
135
|
+
|
|
136
|
+
} catch (e) {
|
|
137
|
+
console.log('[chrome.cookies.merge.Error]', e.message);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* 合并request时自动带上的cookies
|
|
143
|
+
*
|
|
144
|
+
* @param cookie
|
|
145
|
+
* @param domain
|
|
146
|
+
* @returns {*[]|*}
|
|
147
|
+
*/
|
|
148
|
+
async request(cookie, domain) {
|
|
149
|
+
if (!cookie) return [];
|
|
150
|
+
const cookies = cookie.split(';').map(ck => {
|
|
151
|
+
const [name, value] = ck.trim().split('=');
|
|
152
|
+
return {name, value};
|
|
153
|
+
});
|
|
154
|
+
const newMap = new Map(this.cookies.map(obj => [obj.name, obj]));
|
|
155
|
+
cookies.map(obj => {
|
|
156
|
+
const {name, value} = obj;
|
|
157
|
+
if (!newMap.has(obj.name)) {
|
|
158
|
+
newMap.set(obj.name, {name, value, domain})
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const cookies = newMap.get(obj.name);
|
|
162
|
+
if (!cookies.domain) {
|
|
163
|
+
newMap.set(obj.name, {name, value, domain})
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (cookies.value === value) return;
|
|
167
|
+
|
|
168
|
+
const host = '.' + cookies.domain.split('.').slice(-2).join('.');
|
|
169
|
+
if (host === domain) {
|
|
170
|
+
cookies.value = value;
|
|
171
|
+
newMap.set(obj.name, cookies)
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
this.cookies = await Array.from(newMap.values());
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
}
|