Bladeren bron

Merge remote-tracking branch 'origin/master'

dongzhaorui 1 jaar geleden
bovenliggende
commit
2f2c01d860
5 gewijzigde bestanden met toevoegingen van 411 en 132 verwijderingen
  1. 18 5
      zbytb/crawler/defaults.py
  2. 84 2
      zbytb/crawler/sessions_521.py
  3. 14 5
      zbytb/crawler/spiders/ListPageSpider.py
  4. 9 5
      zbytb/main.py
  5. 286 115
      zbytb/package-lock.json

+ 18 - 5
zbytb/crawler/defaults.py

@@ -1,8 +1,9 @@
 import requests
 import urllib3
+import os
+import json
 from requests.models import Response, Request
 
-from crawler.login import update_login_cookies
 from crawler.sessions_521 import http_session_521
 
 urllib3.disable_warnings()
@@ -26,21 +27,33 @@ def prepare_request(
     return request_params
 
 
-def http_request_get(url, account=None, **kwargs):
+def get_cookies(url, headers, proxies=None):
+    if not os.path.isfile(f'./zbytb_ck.json'):
+        http_session_521(url, headers, proxies)
+
+    with open(f'./zbytb_ck.json', 'r', encoding='utf-8') as fr:
+        cks = fr.read()
+    ck = json.loads(cks.replace("'", '"'))
+    return ck
+
+
+def http_request_get(url, **kwargs):
     request_params = prepare_request(**kwargs)
+    headers = request_params.get('headers')
+    proxies = request_params.get('proxies')
     retries = 0
     response = Response()
-    session = requests.Session()
     while retries < 3:
         try:
-            response = session.get(url, **request_params)
+            cks = get_cookies(url,headers, proxies)
+            response = requests.get(url, cookies=cks, **request_params)
             if response.status_code == 200:
                 response.encoding = response.apparent_encoding
                 return True, response
             elif response.status_code == 521:
                 print("****** 521 ******")
                 response.status_code = 10521
-                _, session, _jsl_clearance_s = http_session_521(url, **request_params)
+                _jsl_clearance_s = http_session_521(url, headers, proxies)
                 # TODO 验证是否需要将这个临时变量写入登录cookies,临时写入内存,如有必要再添加
                 # if account is not None:
                 #     update_login_cookies(account, _jsl_clearance_s)

+ 84 - 2
zbytb/crawler/sessions_521.py

@@ -1,6 +1,7 @@
 import copy
 import re
-
+import json
+import time
 import execjs
 import jsbeautifier
 import requests
@@ -9,6 +10,7 @@ from requests.utils import dict_from_cookiejar
 from config.load import node_module_path
 
 
+
 def save_js_script(js_code: str, allow_beautify_code=False):
     with open('etx.js', 'w', encoding='utf-8') as f:
         if allow_beautify_code:
@@ -106,7 +108,7 @@ def extract_cookies_js(js_html: str):
         return None
 
 
-def http_session_521(url: str, headers: dict, **kwargs):
+def http_session_521_old(url: str, headers: dict, **kwargs):
     if 'Cookie' in headers:
         del headers['Cookie']
     if 'cookies' in kwargs:
@@ -151,3 +153,83 @@ def http_session_521(url: str, headers: dict, **kwargs):
     resp2_cookies = dict_from_cookiejar(resp2.cookies)
     resp2_cookies.update({'__jsl_clearance_s': clearance2})
     return True, http_session, resp2_cookies
+
+
+def create_cookie(page_url, headers, proxies=None, is_save_js=False):
+    retry = 0
+    while (retry := retry + 1) < 10:
+        try:
+            session = requests.Session()
+            session.proxies = proxies
+            start_url = page_url
+            res = session.get(start_url, headers=headers, timeout=60, verify=False)
+            js_func = "".join(re.findall("document.cookie=(.*?)location.href", res.text))
+            js_func = 'function sd() { return ' + js_func + "}"
+            ctx = execjs.compile(js_func)
+            sss = ctx.call("sd")
+            cookie = {}
+            for temp, index in res.cookies.get_dict().items():
+                cookie[temp] = index
+
+            for item in sss.split(";"):
+                if '=' in item:
+                    cookie[item.split("=")[0]] = item.split("=")[-1]
+
+            res = session.get(start_url, cookies=cookie, headers=headers, timeout=60, verify=False)
+            html_str = res.content.decode()
+            if "<!DOCTYPE html>" in html_str:
+                html_str = re.sub("<!DOCTYPE html>[\s\S]*?</html>", "", html_str.strip(), re.S)
+
+            if is_save_js:
+                with open('./source_code.js', 'w+', encoding='utf-8') as f:
+                    f.write(html_str)
+
+            js_do_data = "".join(re.findall('};go\((.*?)\)', html_str))
+            js_func = re.sub("<(/*?)script>", "", html_str)
+            location = re.compile('location(.*?)}}else')
+            location2 = re.compile('location(.*?)}else')
+            setTimeout = re.compile('0x5dc;}}(.*?)setTimeout,function\(\)\{')
+            setTimeout2 = re.compile('0x5dc;}(.*?)setTimeout\(function\(\)\{')
+            gox = re.compile('};go(.*?)\)')
+            js_func = re.sub(location, "}}else", js_func)
+            js_func = re.sub(location2, "}else", js_func)
+            js_func = re.sub(setTimeout, "0x5dc;}}", js_func)
+            js_func = re.sub(setTimeout2, "0x5dc;}", js_func)
+            js_func = re.sub(gox, "return document['cookie']\n};", js_func)
+
+            js_func = '''const jsdom = require("jsdom");
+                        const {JSDOM} = jsdom;
+                        const dom = new JSDOM(`<!DOCTYPE html><p>Hello world</p>`,
+                                            {
+                                                url: "https://example.org/",
+                                                referrer: "https://example.com/",
+                                                contentType: "text/html",
+                                            });
+                        window = dom.window;
+                        document = window.document;
+                        location = window.location;
+                        ''' + js_func
+
+            ctx = execjs.compile(js_func, cwd=node_module_path)
+
+            if is_save_js:
+                with open('./clean_code.js', 'w+', encoding='utf-8') as f:
+                    f.write(js_func)
+
+            ss = ctx.call("go", json.loads(js_do_data))
+            for item in ss.split(";"):
+                if '=' in item:
+                    session.cookies.setdefault(item.split("=")[0], item.split("=")[-1])
+            session.get(start_url, headers=headers, timeout=60, verify=False)
+            cookies = session.cookies.get_dict()
+            return cookies
+        except Exception as e:
+            print("cookie生产错误!")
+            time.sleep(3)
+
+
+def http_session_521(url: str, headers: dict, proxies=None):
+    resp2_cookies = create_cookie(page_url=url, headers=headers, proxies=proxies)
+    with open(f'./zbytb_ck.json', 'w', encoding='utf-8') as fw:
+        fw.write(json.dumps(resp2_cookies))
+    return resp2_cookies

+ 14 - 5
zbytb/crawler/spiders/ListPageSpider.py

@@ -28,7 +28,7 @@ class CrawlListPageSpider:
         self.redis_key = 'zbytb_2024'
         self.r = redis_client()
 
-        self.host = 'https://www.zbytb.com/search'
+        self.host = "https://www.zbytb.com/search"
         self.headers = kwargs.get('headers') or headers
         self.proxy = Proxy(enable_proxy)
         self.max_page = kwargs.get('max_page', 10)
@@ -37,6 +37,7 @@ class CrawlListPageSpider:
     def crawl_request(self, url, **kwargs):
         retries = 0
         while True:
+            print(self.proxy.proxies)
             success, response = http_request_get(
                 url,
                 headers=kwargs.get('headers'),
@@ -111,7 +112,7 @@ class CrawlListPageSpider:
         label_info = {}
         if module_id == 27:
             label_info = {
-                'type_code': 25,  # 详情api中使用参数
+                'type_code': 27,  # 详情api中使用参数
                 'channel': '询价',
                 'spidercode': 'a_zgzbycgw_zbxx_xj',
             }
@@ -133,10 +134,18 @@ class CrawlListPageSpider:
                 'channel': '拟在建',
                 'spidercode': 'a_zgzbycgw_zbxx_nzj',
             }
+
         params = {
-            'moduleid': module_id,
-            'areaids': area_id,
-            'page': page,
+            "kw": "",
+            "okw": "",
+            "catid": "0",
+            "zizhi": "",
+            "zdxm": "",
+            "zdyear": "",
+            "field": "",
+            "moduleid": module_id,
+            "areaids": area_id,
+            "page": page
         }
         referer = url = '{}/?{}'.format(self.host, urlencode(params))
         self.headers.update({'Referer': referer})

+ 9 - 5
zbytb/main.py

@@ -5,10 +5,14 @@ from crawler.spiders.ListPageSpider import CrawlListPageSpider
 def list_page_spider():
     headers = {
         'Host': 'www.zbytb.com',
-        'Upgrade-Insecure-Requests': '1',
-        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36',
-        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
-        'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
+        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
+        "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
+        "Cache-Control": "no-cache",
+        "Connection": "keep-alive",
+        "Pragma": "no-cache",
+        "Referer": "https://www.zbytb.com/search/",
+        "Upgrade-Insecure-Requests": "1",
+        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
     }
     CrawlListPageSpider(
         db='py_spider',
@@ -31,4 +35,4 @@ def detail_page_spider():
 
 
 if __name__ == '__main__':
-    detail_page_spider()
+    list_page_spider()

+ 286 - 115
zbytb/package-lock.json

@@ -1,219 +1,306 @@
 {
   "name": "zbytb",
   "version": "1.0.0",
-  "lockfileVersion": 1,
+  "lockfileVersion": 3,
   "requires": true,
-  "dependencies": {
-    "@tootallnate/once": {
+  "packages": {
+    "": {
+      "name": "zbytb",
+      "version": "1.0.0",
+      "license": "ISC",
+      "dependencies": {
+        "jsdom": "^19.0.0"
+      }
+    },
+    "node_modules/@tootallnate/once": {
       "version": "2.0.0",
       "resolved": "https://registry.npmmirror.com/@tootallnate/once/-/once-2.0.0.tgz",
-      "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A=="
+      "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
+      "engines": {
+        "node": ">= 10"
+      }
     },
-    "abab": {
+    "node_modules/abab": {
       "version": "2.0.5",
       "resolved": "https://registry.npmmirror.com/abab/-/abab-2.0.5.tgz",
-      "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q=="
+      "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==",
+      "deprecated": "Use your platform's native atob() and btoa() methods instead"
     },
-    "acorn": {
+    "node_modules/acorn": {
       "version": "8.7.0",
       "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.7.0.tgz",
-      "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ=="
+      "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==",
+      "bin": {
+        "acorn": "bin/acorn"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
     },
-    "acorn-globals": {
+    "node_modules/acorn-globals": {
       "version": "6.0.0",
       "resolved": "https://registry.npmmirror.com/acorn-globals/-/acorn-globals-6.0.0.tgz",
       "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==",
-      "requires": {
+      "dependencies": {
         "acorn": "^7.1.1",
         "acorn-walk": "^7.1.1"
+      }
+    },
+    "node_modules/acorn-globals/node_modules/acorn": {
+      "version": "7.4.1",
+      "resolved": "https://registry.npmmirror.com/acorn/-/acorn-7.4.1.tgz",
+      "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+      "bin": {
+        "acorn": "bin/acorn"
       },
-      "dependencies": {
-        "acorn": {
-          "version": "7.4.1",
-          "resolved": "https://registry.npmmirror.com/acorn/-/acorn-7.4.1.tgz",
-          "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="
-        }
+      "engines": {
+        "node": ">=0.4.0"
       }
     },
-    "acorn-walk": {
+    "node_modules/acorn-walk": {
       "version": "7.2.0",
       "resolved": "https://registry.npmmirror.com/acorn-walk/-/acorn-walk-7.2.0.tgz",
-      "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA=="
+      "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==",
+      "engines": {
+        "node": ">=0.4.0"
+      }
     },
-    "agent-base": {
+    "node_modules/agent-base": {
       "version": "6.0.2",
       "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-6.0.2.tgz",
       "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
-      "requires": {
+      "dependencies": {
         "debug": "4"
+      },
+      "engines": {
+        "node": ">= 6.0.0"
       }
     },
-    "asynckit": {
+    "node_modules/asynckit": {
       "version": "0.4.0",
       "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
       "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
     },
-    "browser-process-hrtime": {
+    "node_modules/browser-process-hrtime": {
       "version": "1.0.0",
       "resolved": "https://registry.npmmirror.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz",
       "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow=="
     },
-    "combined-stream": {
+    "node_modules/combined-stream": {
       "version": "1.0.8",
       "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
       "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
-      "requires": {
+      "dependencies": {
         "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
       }
     },
-    "cssom": {
+    "node_modules/cssom": {
       "version": "0.5.0",
       "resolved": "https://registry.npmmirror.com/cssom/-/cssom-0.5.0.tgz",
       "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw=="
     },
-    "cssstyle": {
+    "node_modules/cssstyle": {
       "version": "2.3.0",
       "resolved": "https://registry.npmmirror.com/cssstyle/-/cssstyle-2.3.0.tgz",
       "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==",
-      "requires": {
+      "dependencies": {
         "cssom": "~0.3.6"
       },
-      "dependencies": {
-        "cssom": {
-          "version": "0.3.8",
-          "resolved": "https://registry.npmmirror.com/cssom/-/cssom-0.3.8.tgz",
-          "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg=="
-        }
+      "engines": {
+        "node": ">=8"
       }
     },
-    "data-urls": {
+    "node_modules/cssstyle/node_modules/cssom": {
+      "version": "0.3.8",
+      "resolved": "https://registry.npmmirror.com/cssom/-/cssom-0.3.8.tgz",
+      "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg=="
+    },
+    "node_modules/data-urls": {
       "version": "3.0.1",
       "resolved": "https://registry.npmmirror.com/data-urls/-/data-urls-3.0.1.tgz",
       "integrity": "sha512-Ds554NeT5Gennfoo9KN50Vh6tpgtvYEwraYjejXnyTpu1C7oXKxdFk75REooENHE8ndTVOJuv+BEs4/J/xcozw==",
-      "requires": {
+      "dependencies": {
         "abab": "^2.0.3",
         "whatwg-mimetype": "^3.0.0",
         "whatwg-url": "^10.0.0"
+      },
+      "engines": {
+        "node": ">=12"
       }
     },
-    "debug": {
+    "node_modules/debug": {
       "version": "4.3.3",
       "resolved": "https://registry.npmmirror.com/debug/-/debug-4.3.3.tgz",
       "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
-      "requires": {
+      "dependencies": {
         "ms": "2.1.2"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
       }
     },
-    "decimal.js": {
+    "node_modules/decimal.js": {
       "version": "10.3.1",
       "resolved": "https://registry.npmmirror.com/decimal.js/-/decimal.js-10.3.1.tgz",
       "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ=="
     },
-    "deep-is": {
+    "node_modules/deep-is": {
       "version": "0.1.4",
       "resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz",
       "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="
     },
-    "delayed-stream": {
+    "node_modules/delayed-stream": {
       "version": "1.0.0",
       "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
-      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+      "engines": {
+        "node": ">=0.4.0"
+      }
     },
-    "domexception": {
+    "node_modules/domexception": {
       "version": "4.0.0",
       "resolved": "https://registry.npmmirror.com/domexception/-/domexception-4.0.0.tgz",
       "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==",
-      "requires": {
+      "deprecated": "Use your platform's native DOMException instead",
+      "dependencies": {
         "webidl-conversions": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=12"
       }
     },
-    "escodegen": {
+    "node_modules/escodegen": {
       "version": "2.0.0",
       "resolved": "https://registry.npmmirror.com/escodegen/-/escodegen-2.0.0.tgz",
       "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==",
-      "requires": {
+      "dependencies": {
         "esprima": "^4.0.1",
         "estraverse": "^5.2.0",
         "esutils": "^2.0.2",
-        "optionator": "^0.8.1",
+        "optionator": "^0.8.1"
+      },
+      "bin": {
+        "escodegen": "bin/escodegen.js",
+        "esgenerate": "bin/esgenerate.js"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "optionalDependencies": {
         "source-map": "~0.6.1"
       }
     },
-    "esprima": {
+    "node_modules/esprima": {
       "version": "4.0.1",
       "resolved": "https://registry.npmmirror.com/esprima/-/esprima-4.0.1.tgz",
-      "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
+      "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+      "bin": {
+        "esparse": "bin/esparse.js",
+        "esvalidate": "bin/esvalidate.js"
+      },
+      "engines": {
+        "node": ">=4"
+      }
     },
-    "estraverse": {
+    "node_modules/estraverse": {
       "version": "5.3.0",
       "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz",
-      "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="
+      "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+      "engines": {
+        "node": ">=4.0"
+      }
     },
-    "esutils": {
+    "node_modules/esutils": {
       "version": "2.0.3",
       "resolved": "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz",
-      "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="
+      "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
     },
-    "fast-levenshtein": {
+    "node_modules/fast-levenshtein": {
       "version": "2.0.6",
       "resolved": "https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
       "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="
     },
-    "form-data": {
+    "node_modules/form-data": {
       "version": "4.0.0",
       "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.0.tgz",
       "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
-      "requires": {
+      "dependencies": {
         "asynckit": "^0.4.0",
         "combined-stream": "^1.0.8",
         "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
       }
     },
-    "html-encoding-sniffer": {
+    "node_modules/html-encoding-sniffer": {
       "version": "3.0.0",
       "resolved": "https://registry.npmmirror.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz",
       "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==",
-      "requires": {
+      "dependencies": {
         "whatwg-encoding": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=12"
       }
     },
-    "http-proxy-agent": {
+    "node_modules/http-proxy-agent": {
       "version": "5.0.0",
       "resolved": "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
       "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
-      "requires": {
+      "dependencies": {
         "@tootallnate/once": "2",
         "agent-base": "6",
         "debug": "4"
+      },
+      "engines": {
+        "node": ">= 6"
       }
     },
-    "https-proxy-agent": {
+    "node_modules/https-proxy-agent": {
       "version": "5.0.0",
       "resolved": "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
       "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==",
-      "requires": {
+      "dependencies": {
         "agent-base": "6",
         "debug": "4"
+      },
+      "engines": {
+        "node": ">= 6"
       }
     },
-    "iconv-lite": {
+    "node_modules/iconv-lite": {
       "version": "0.6.3",
       "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz",
       "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
-      "requires": {
+      "dependencies": {
         "safer-buffer": ">= 2.1.2 < 3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
       }
     },
-    "is-potential-custom-element-name": {
+    "node_modules/is-potential-custom-element-name": {
       "version": "1.0.1",
       "resolved": "https://registry.npmmirror.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
       "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="
     },
-    "jsdom": {
+    "node_modules/jsdom": {
       "version": "19.0.0",
       "resolved": "https://registry.npmmirror.com/jsdom/-/jsdom-19.0.0.tgz",
       "integrity": "sha512-RYAyjCbxy/vri/CfnjUWJQQtZ3LKlLnDqj+9XLNnJPgEGeirZs3hllKR20re8LUZ6o1b1X4Jat+Qd26zmP41+A==",
-      "requires": {
+      "dependencies": {
         "abab": "^2.0.5",
         "acorn": "^8.5.0",
         "acorn-globals": "^6.0.0",
@@ -241,187 +328,271 @@
         "whatwg-url": "^10.0.0",
         "ws": "^8.2.3",
         "xml-name-validator": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "peerDependencies": {
+        "canvas": "^2.5.0"
+      },
+      "peerDependenciesMeta": {
+        "canvas": {
+          "optional": true
+        }
       }
     },
-    "levn": {
+    "node_modules/levn": {
       "version": "0.3.0",
       "resolved": "https://registry.npmmirror.com/levn/-/levn-0.3.0.tgz",
       "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==",
-      "requires": {
+      "dependencies": {
         "prelude-ls": "~1.1.2",
         "type-check": "~0.3.2"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
       }
     },
-    "mime-db": {
+    "node_modules/mime-db": {
       "version": "1.52.0",
       "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
-      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "engines": {
+        "node": ">= 0.6"
+      }
     },
-    "mime-types": {
+    "node_modules/mime-types": {
       "version": "2.1.35",
       "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
       "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
-      "requires": {
+      "dependencies": {
         "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
       }
     },
-    "ms": {
+    "node_modules/ms": {
       "version": "2.1.2",
       "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz",
       "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
     },
-    "nwsapi": {
+    "node_modules/nwsapi": {
       "version": "2.2.0",
       "resolved": "https://registry.npmmirror.com/nwsapi/-/nwsapi-2.2.0.tgz",
       "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ=="
     },
-    "optionator": {
+    "node_modules/optionator": {
       "version": "0.8.3",
       "resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.8.3.tgz",
       "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
-      "requires": {
+      "dependencies": {
         "deep-is": "~0.1.3",
         "fast-levenshtein": "~2.0.6",
         "levn": "~0.3.0",
         "prelude-ls": "~1.1.2",
         "type-check": "~0.3.2",
         "word-wrap": "~1.2.3"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
       }
     },
-    "parse5": {
+    "node_modules/parse5": {
       "version": "6.0.1",
       "resolved": "https://registry.npmmirror.com/parse5/-/parse5-6.0.1.tgz",
       "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="
     },
-    "prelude-ls": {
+    "node_modules/prelude-ls": {
       "version": "1.1.2",
       "resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.1.2.tgz",
-      "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w=="
+      "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==",
+      "engines": {
+        "node": ">= 0.8.0"
+      }
     },
-    "psl": {
+    "node_modules/psl": {
       "version": "1.8.0",
       "resolved": "https://registry.npmmirror.com/psl/-/psl-1.8.0.tgz",
       "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ=="
     },
-    "punycode": {
+    "node_modules/punycode": {
       "version": "2.1.1",
       "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.1.1.tgz",
-      "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
+      "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+      "engines": {
+        "node": ">=6"
+      }
     },
-    "safer-buffer": {
+    "node_modules/safer-buffer": {
       "version": "2.1.2",
       "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz",
       "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
     },
-    "saxes": {
+    "node_modules/saxes": {
       "version": "5.0.1",
       "resolved": "https://registry.npmmirror.com/saxes/-/saxes-5.0.1.tgz",
       "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==",
-      "requires": {
+      "dependencies": {
         "xmlchars": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=10"
       }
     },
-    "source-map": {
+    "node_modules/source-map": {
       "version": "0.6.1",
       "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz",
       "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-      "optional": true
+      "optional": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
     },
-    "symbol-tree": {
+    "node_modules/symbol-tree": {
       "version": "3.2.4",
       "resolved": "https://registry.npmmirror.com/symbol-tree/-/symbol-tree-3.2.4.tgz",
       "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="
     },
-    "tough-cookie": {
+    "node_modules/tough-cookie": {
       "version": "4.0.0",
       "resolved": "https://registry.npmmirror.com/tough-cookie/-/tough-cookie-4.0.0.tgz",
       "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==",
-      "requires": {
+      "dependencies": {
         "psl": "^1.1.33",
         "punycode": "^2.1.1",
         "universalify": "^0.1.2"
+      },
+      "engines": {
+        "node": ">=6"
       }
     },
-    "tr46": {
+    "node_modules/tr46": {
       "version": "3.0.0",
       "resolved": "https://registry.npmmirror.com/tr46/-/tr46-3.0.0.tgz",
       "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==",
-      "requires": {
+      "dependencies": {
         "punycode": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=12"
       }
     },
-    "type-check": {
+    "node_modules/type-check": {
       "version": "0.3.2",
       "resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.3.2.tgz",
       "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==",
-      "requires": {
+      "dependencies": {
         "prelude-ls": "~1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
       }
     },
-    "universalify": {
+    "node_modules/universalify": {
       "version": "0.1.2",
       "resolved": "https://registry.npmmirror.com/universalify/-/universalify-0.1.2.tgz",
-      "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
+      "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+      "engines": {
+        "node": ">= 4.0.0"
+      }
     },
-    "w3c-hr-time": {
+    "node_modules/w3c-hr-time": {
       "version": "1.0.2",
       "resolved": "https://registry.npmmirror.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
       "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==",
-      "requires": {
+      "deprecated": "Use your platform's native performance.now() and performance.timeOrigin.",
+      "dependencies": {
         "browser-process-hrtime": "^1.0.0"
       }
     },
-    "w3c-xmlserializer": {
+    "node_modules/w3c-xmlserializer": {
       "version": "3.0.0",
       "resolved": "https://registry.npmmirror.com/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz",
       "integrity": "sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg==",
-      "requires": {
+      "dependencies": {
         "xml-name-validator": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=12"
       }
     },
-    "webidl-conversions": {
+    "node_modules/webidl-conversions": {
       "version": "7.0.0",
       "resolved": "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
-      "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="
+      "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
+      "engines": {
+        "node": ">=12"
+      }
     },
-    "whatwg-encoding": {
+    "node_modules/whatwg-encoding": {
       "version": "2.0.0",
       "resolved": "https://registry.npmmirror.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz",
       "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==",
-      "requires": {
+      "dependencies": {
         "iconv-lite": "0.6.3"
+      },
+      "engines": {
+        "node": ">=12"
       }
     },
-    "whatwg-mimetype": {
+    "node_modules/whatwg-mimetype": {
       "version": "3.0.0",
       "resolved": "https://registry.npmmirror.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz",
-      "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q=="
+      "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==",
+      "engines": {
+        "node": ">=12"
+      }
     },
-    "whatwg-url": {
+    "node_modules/whatwg-url": {
       "version": "10.0.0",
       "resolved": "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-10.0.0.tgz",
       "integrity": "sha512-CLxxCmdUby142H5FZzn4D8ikO1cmypvXVQktsgosNy4a4BHrDHeciBBGZhb0bNoR5/MltoCatso+vFjjGx8t0w==",
-      "requires": {
+      "dependencies": {
         "tr46": "^3.0.0",
         "webidl-conversions": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=12"
       }
     },
-    "word-wrap": {
+    "node_modules/word-wrap": {
       "version": "1.2.3",
       "resolved": "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.3.tgz",
-      "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ=="
+      "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
     },
-    "ws": {
+    "node_modules/ws": {
       "version": "8.5.0",
       "resolved": "https://registry.npmmirror.com/ws/-/ws-8.5.0.tgz",
-      "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg=="
+      "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==",
+      "engines": {
+        "node": ">=10.0.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": "^5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
     },
-    "xml-name-validator": {
+    "node_modules/xml-name-validator": {
       "version": "4.0.0",
       "resolved": "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
-      "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw=="
+      "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==",
+      "engines": {
+        "node": ">=12"
+      }
     },
-    "xmlchars": {
+    "node_modules/xmlchars": {
       "version": "2.2.0",
       "resolved": "https://registry.npmmirror.com/xmlchars/-/xmlchars-2.2.0.tgz",
       "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="