{"id":342050,"date":"2022-12-01T21:00:15","date_gmt":"2022-12-01T21:00:15","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=342050"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=342050","title":{"rendered":"<span>server-queryselector aka \u043f\u0430\u0440\u0441\u0438\u043c html \u0432 nodejs<\/span>"},"content":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0418\u0442\u0430\u043a, \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u0441 \u0432\u0435\u0431 \u0441\u0430\u0439\u0442\u0430 \u2014 \u044d\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0432 3 \u0448\u0430\u0433\u0430<\/p>\n<p>1) \u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c html \u0441\u0430\u0439\u0442\u0430 (\u043f\u0440\u043e\u043f\u0443\u0441\u0442\u0438\u043c \u044d\u0442\u043e\u0442 \u0448\u0430\u0433)<\/p>\n<p>2) \u0420\u0430\u0441\u043f\u0430\u0440\u0441\u0438\u0442\u044c html \u0441\u0442\u0440\u043e\u043a\u0443 \u0438 \u0441\u043e\u0437\u0434\u0430\u0442\u044c dom. \u2014 builderdom.js<\/p>\n<p>3) \u041d\u0430\u0439\u0442\u0438 \u043d\u0443\u0436\u043d\u044b\u0435 dom_node \u0438\u0437 dom \u043f\u043e \u043a\u0441\u0441\u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u0430\u043c.<\/p>\n<p>3.1) \u0420\u0430\u0441\u043f\u0430\u0440\u0441\u0438\u0442\u044c \u0441\u0442\u0440\u043e\u043a\u0443 \u043a\u0441\u0441\u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u043e\u0432 \u0438 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0434\u0435\u0440\u0435\u0432\u043e \u0434\u043b\u044f \u043f\u043e\u0438\u0441\u043a\u0430. \u2014 cssselectorparser.js<br \/>3.2) \u041e\u0442\u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u043e\u043c_\u043d\u043e\u0434\u044b \u043f\u043e \u0434\u0435\u0440\u0435\u0432\u0443 \u043a\u0441\u0441\u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u043e\u0432 \u0438 \u043d\u0430\u0439\u0442\u0438 \u043d\u0443\u0436\u043d\u044b\u0435. \u2014 treeworker.js  <\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/b22\/7b4\/a95\/b227b4a95147fab72bf99533c8906236.png\" width=\"386\" height=\"80\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/b22\/7b4\/a95\/b227b4a95147fab72bf99533c8906236.png\"\/><figcaption><\/figcaption><\/figure>\n<p>2) \u041f\u0430\u0440\u0441\u0438\u043c html:  <\/p>\n<p>2.1) \u041d\u0430\u0440\u0435\u0437\u0430\u0435\u043c \u0441\u0442\u0440\u043e\u043a\u0438(\u0432\u044b\u0434\u0435\u043b\u0438\u043b \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442\u00a0<a href=\"https:\/\/href.li\/?https:\/\/github.com\/ru51a4\/superxmlparser74\" rel=\"noopener noreferrer nofollow\">superxmlparser74<\/a>) <br \/>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0441\u0442\u0440\u043e\u043a\u0438, \u043d\u0430\u043a\u0430\u043f\u043b\u0438\u0432\u0430\u0435\u043c \u0432 \u043d\u0438\u0445 \u0442\u043e\u043a\u0435\u043d\u044b \u0438 \u043e\u0431\u0440\u0435\u0437\u0430\u0435\u043c \u043f\u043e \u043c\u0430\u0440\u043a\u0435\u0440\u0430\u043c<\/p>\n<p>\u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0442\u0435\u0433\/innerTEXT \u2014 t, \u0430\u0442\u0442\u0440\u0438\u0431\u0443\u0442\u044b \u0432 \u0432\u0438\u0434\u0435 \u043c\u0430\u0441\u0441\u0438\u0432\u0430 \u2014 attr<\/p>\n<details class=\"spoiler\">\n<summary>\u043a\u043b\u0438\u043a<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">class superxmlparser74 {     static parse(str, cbOpenTag, cbInnerText, cbClosedTag, cbSelfOpenTag = () => {     }) {         let isOpen = false;         let startAttr = false;         let t = ''         let tAttrKey = '';         let tAttrValue = '';         let tAttrStart = false;         let tAttr = '';         let attr = [];         let prevCh = '';         for (let i = 0; i &lt;= str.length - 1; i++) {             \/\/(1)&lt;li (2)class=\"breadcrumb-item-selected text-gray-light breadcrumb-item text-mono h5-mktg\" aria-current=\"GitHub Student Developer Pack\"(3)>GitHub Student Developer Pack(4)&lt;\/li(5)>             \/\/&lt;selfclosing \/>             \/\/comments \/\/ &lt;!-- -->             if (str[i] === '\/' &amp;&amp; str[i + 1] === \"\/\") {                 for (let j = i + 2; j &lt;= str.length - 1; j++) {                     if (str[j] === '\\n') {                         i = j;                         break;                     }                 }                 continue             } else if (str[i] === \"&lt;\") { \/\/1                 \/\/comments &lt;!-- -->                 if (str[i + 1] === '!' &amp;&amp; str[i + 2] === \"-\" &amp;&amp; str[i + 3] === \"-\") {                     for (let j = i + 4; j &lt;= str.length - 1; j++) {                         if (str[j] === '-' &amp;&amp; str[j + 1] === '-' &amp;&amp; str[j + 2] === '>') {                             i = j + 2;                             break;                         }                     }                     continue                 }                 \/\/\/                  if (t.trim() !== '' &amp;&amp; t.trim() !== \"\\n\" &amp;&amp; t.trim() !== \"\\t\") {                     \/\/cut innerTEXT 4                     cbInnerText({                         value: t                     });                     t = '';                 } else if (str[i + 1] !== \"\/\") {                     cbInnerText({                         value: \"\"                     });                 }                 \/\/open tag                 isOpen = true;                 if (str[i + 1] === \"\/\") {                     isOpen = false;                     i = i + 1;                     continue;                 }             } else if (str[i] === '>') {                 \/\/\/closed tag - build 3\/5                 if (isOpen) {                     if (prevCh === \"\/\") {                         cbSelfOpenTag({                             tag: t,                             attr: attr                         })                     } else {                         cbOpenTag({                             tag: t,                             attr: attr,                         })                     }                 } else {                     cbClosedTag({})                 }                 attr = [];                 t = '';                 startAttr = false;                 isOpen = false;             } else {                 \/\/accum str                 if ((!startAttr &amp;&amp; str[i] !== ' ') || !isOpen) {                     t += str[i];                 } else if (startAttr) { \/\/get attr 2                     if (str[i] === '=') {                         tAttrKey = tAttr                         tAttr = '';                     } else if (str[i] === '\"') {                         tAttrStart = !tAttrStart;                         if (tAttrStart === false) {                             if (tAttrKey === 'class') {                                 tAttrValue = tAttr.split(\" \");                             } else {                                 tAttrValue = [tAttr];                             }                             tAttr = '';                             attr.push({key: tAttrKey, value: tAttrValue});                             if (str[i + 1] === ' ') {                                 i = i + 1;                                 continue;                             }                         }                     } else {                         tAttr += str[i];                     }                  } else if (str[i] === ' ' &amp;&amp; isOpen) {                     startAttr = true;                 }              }             prevCh = str[i];         }     } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>2.2) \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0434\u0435\u0440\u0435\u0432\u043e<\/p>\n<pre><code class=\"javascript\">const superxmlparser74 = require(\"superxmlparser74\");  class dom_node {     childrens = [];     innerTEXT = '';     tag;     treeWorker;      constructor() {         this.treeWorker = global.treeworker;     }      innerHTML = (cliFormat = false) => {         return this.treeWorker.getInnerHTML(this, cliFormat);     };     querySelector = (selector) => {         this.treeWorker.setCurrentTreeByNode(this);         return this.treeWorker.filtredBySelector(selector);     } }  class BuilderDOM {     html_to_dom(str) {         var utils = {             noEndTag(tag) {                 let noEndTags = [                     'noscript',                     'link',                     'base',                     'meta',                     'input',                     'svg',                     'path',                     'img',                     'br',                     'area',                     'base',                     'br',                     'col',                     'embed',                     'hr',                     'img',                     'input',                     'keygen',                     'link',                     'meta',                     'param',                     'source',                     'track',                     'wbr'                 ];                 return noEndTags.includes(tag);             }         };          let res = [];         let parentStack = [];         superxmlparser74.parse(str,             (item) => {                 \/\/opentag                 if (item.tag === 'p' &amp;&amp; parentStack[parentStack.length - 1]?.tag === 'p') {                     parentStack.pop();                 }                 \/\/                 let el = new dom_node();                 el.attr = item.attr;                 el.tag = item.tag;                 res.push(el);                 el.attr.push({                     key: 'tag',                     value: [item.tag]                 })                 if (parentStack[parentStack.length - 1] &amp;&amp; el.tag !== 'script') {                     parentStack[parentStack.length - 1].childrens.push(el)                 }                 if (!utils.noEndTag(el.tag)) {                     parentStack.push(el);                 }             },             (item) => {                 \/\/innertext                 if (parentStack[parentStack.length - 1]) {                     parentStack[parentStack.length - 1].innerTEXT += item.value;                 }             },             (item) => {                 \/\/closedtag                 parentStack.pop();             });          return res;     }  }<\/code><\/pre>\n<p>3) \u041f\u043e\u0438\u0441\u043a<\/p>\n<p>3.1) \u041f\u0430\u0440\u0441\u0438\u043d\u0433 \u043a\u0441\u0441\u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u043e\u0432  <\/p>\n<p>\u0420\u0430\u0437\u0431\u0438\u0432\u0430\u0435\u043c \u0441\u0442\u0440\u043e\u043a\u0443 \u043a\u0441\u0441\u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u043e\u0432 \u043f\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044f\u043c, \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u043a\u0430\u043a\u043e\u0439 \u044d\u0442\u043e \u043a\u0441\u0441\u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440, \u043e\u0431\u0440\u0435\u0437\u0430\u0435\u043c \u0438 \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0434\u0435\u0440\u0435\u0432\u043e.  <\/p>\n<pre><code class=\"javascript\">class cssSelectorParser {     parse(str) {         let res = [];         str = this.utils.lex(str);         for (var i = 0; i &lt;= str.length - 1; i++) {             if (str[i].includes(\".\")) {                 res.push({key: 'class', value: str[i].substring(1)});             } else if (str[i].includes(\"#\")) {                 res.push({key: 'id', value: str[i].substring(1)});             } else if (str[i].includes(\"[\")) {                 let current = str[i];                 current = current.substring(1);                 current = current.slice(0, -1);                 current = current.split(\"=\");                 res.push({key: current[0], value: current[1]});             } else if (str[i] === '>') {                 res.push({key: '', value: str[i]});             } else if (str[i] === ' ') {                 res.push({key: '', value: str[i]});             } else if(str[i] !== '') {                 res.push({key: 'tag', value: str[i]});             }         }         \/\/merge         let mergeRes = [];         let t = [];         for (var i = 0; i &lt;= res.length - 1; i++) {             if (res[i].value === ' ') {                 mergeRes.push(t);                 t = [];             } else {                 t.push(res[i]);             }         }         mergeRes.push(t);         \/\/         return mergeRes;     }       utils = {         lex(str) {             let res = '';             for (var i = 0; i &lt;= str.length - 1; i++) {                 res += str[i];                 if (str[i + 1] === \".\" || str[i + 1] === '#' || str[i + 1] === '>' || str[i + 1] === '[' || (str[i] === ' ')) {                     res += \"\\n\";                 } else if (str[i + 1] === \" \") {                     res += \"\\n\"                 }             }             return res.split(\"\\n\");         }     } }<\/code><\/pre>\n<p>3.2) \u0422\u0435\u043f\u0435\u0440\u044c \u043e\u0442\u0444\u0438\u043b\u044c\u0442\u0440\u0443\u0435\u043c \u0434\u043e\u043c_\u043d\u043e\u0434\u044b \u043f\u043e \u043a\u0441\u0441\u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u0430\u043c  <\/p>\n<pre><code class=\"javascript\">class treeWorker {     \/\/\u0422\u0435\u043a\u0443\u0449\u0438\u0439 \u043c\u0430\u0441\u0441\u0438\u0432 \u0434\u043e\u043c_\u043d\u043e\u0434\u0435     _tree;       \/\/\u041f\u043e\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043c\u0430\u0441\u0441\u0438\u0432 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0432\u0441\u0435\u0445 \u0434\u0435\u0442\u0435\u0439 \u043d\u043e\u0434\u044b     setCurrentTreeByNode(node) {         let tree = this._getChildrens([node]);         this._tree = tree;     }     \/\/\u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0446\u0438\u043a\u043b, \u0433\u0434\u0435 \u043c\u044b \u0438 \u0444\u0438\u043b\u044c\u0442\u0440\u0443\u0435\u043c dom \u043f\u043e \u0434\u0435\u0440\u0435\u0432\u0443 \u043a\u0441\u0441\u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u043e\u0432     filtredBySelector(selector) {         let cssselectorParser = new cssSelectorParser();         selector = cssselectorParser.parse(selector);         let res;         for (let i = 0; i &lt;= selector.length - 1; i++) {             let currentSelector = selector[i];             let key;             let item;             let isArrowSelector = (currentSelector[0].value === '>');             if (isArrowSelector) {                 continue;             }             for (var j = 0; j &lt; currentSelector.length; j++) {                 key = currentSelector[j].key                 item = currentSelector[j].value;                 this._filtredByAttribute(key, item)             }             res = this._tree;             let nextSelectorArrow = selector[i + 1] &amp;&amp; selector[i + 1][0] &amp;&amp; selector[i + 1][0].value === '>';             this._sliceChildrens(nextSelectorArrow)         }         return res;     }       \/\/\u041f\u043e\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0432\u0435\u0441\u044c \u0445\u0442\u043c\u043b \u043d\u043e\u0434\u044b     getInnerHTML(dom_node, cliFormat = false) {         let res = '';         let lvl = -1;         function deep(node) {             let leftMargin = '';             for (let i = 0; i &lt;= lvl; i++) {                 leftMargin += (cliFormat) ? '   ' : '';             }             res += leftMargin + '&lt;' + node.tag + \">\"             res += (cliFormat) ? \"\\n\" : \"\";             res += (cliFormat &amp;&amp; node.innerTEXT !== '') ? leftMargin + '   ' : '';             res += node.innerTEXT;             res += (cliFormat &amp;&amp; node.innerTEXT !== '') ? \"\\n\" : \"\";             node.childrens.forEach((childNode) => {                 lvl++;                 deep(childNode);                 lvl--;             });             res += leftMargin + '';             res += (cliFormat &amp;&amp; lvl !== -1) ? \"\\n\" : \"\";         }           deep(dom_node);         return res;     }       \/\/\u0424\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u044f \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u043c\u0430\u0441\u0441\u0438\u0432\u0430 \u0434\u043e\u043c_\u043d\u043e\u0434\u0435 \u043f\u043e \u0430\u0442\u0442\u0440\u0438\u0431\u0443\u0442\u0430\u043c     _filtredByAttribute(_key, _value) {         this._tree = this._tree.filter((item) => {             let currentAttr = item.attr.find((attr) => attr.key === _key);             if (currentAttr) {                 return currentAttr.value.includes(_value.trim())             }         });     }     \/\/\u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0434\u0435\u0442\u0435\u0439(\u043f\u0435\u0440\u0432\u044b\u0439 \u0441\u0440\u0435\u0437 \u0438\u043b\u0438 \u0432\u0435\u0441\u044c) \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u043c\u0430\u0441\u0441\u0438\u0432\u0430 \u0434\u043e\u043c_\u043d\u043e\u0434\u0435     _sliceChildrens(firstChild = false) {         let res = [];         if (firstChild) {             for (let i = 0; i &lt;= this._tree.length - 1; i++) {                 res.push(...this._tree[i].childrens);             }         } else {             res = this._getChildrens(this._tree)         }         this._tree = res;     }       \/\/\u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0432\u0441\u0435\u0445 \u0434\u0435\u0442\u0435\u0439 \u0434\u043e\u043c \u043d\u043e\u0434     _getChildrens(currentNodes) {         \/\/get all childs         let allChilds = [...currentNodes];         let queue = [...currentNodes];         while(queue.length){             let item = queue.shift();             for(let i = 0; i &lt;= item.childrens.length - 1; i++){                 queue.push(item.childrens[i]);                 allChilds.push(item.childrens[i]);             }         }         return allChilds;     }   }<\/code><\/pre>\n<p>\u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u2014 \u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0446\u0438\u043a\u043b, \u0433\u0434\u0435 \u043c\u044b \u0438 \u0444\u0438\u043b\u044c\u0442\u0440\u0443\u0435\u043c \u00ab\u0442\u0435\u043a\u0443\u0449\u0438\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b dom\u00bb \u043f\u043e \u0434\u0435\u0440\u0435\u0432\u0443 \u043a\u0441\u0441\u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u043e\u0432.<\/p>\n<p>\/\/<br \/><strong>\u0425\u0440\u0430\u043d\u0438\u043c \u0442\u0435\u043a\u0443\u0449\u0438\u0435 \u0434\u043e\u043c_\u043d\u043e\u0434\u044b \u0432 this._tree, \u0444\u0438\u043b\u044c\u0442\u0440\u0443\u0435\u043c \u0438\u0445, \u043d\u0430\u0440\u0435\u0437\u0430\u0435\u043c \u0434\u0435\u0442\u0435\u0439, \u0440\u0435\u043f\u0438\u0442<\/strong>  <\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/3f1\/149\/d91\/3f1149d91d95e34febb7bddec3ec9835.png\" width=\"471\" height=\"344\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/3f1\/149\/d91\/3f1149d91d95e34febb7bddec3ec9835.png\"\/><figcaption><\/figcaption><\/figure>\n<pre><code class=\"javascript\">filtredBySelector(selector) {         let cssselectorParser = new cssSelectorParser();         \/\/\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0434\u0435\u0440\u0435\u0432\u043e \u043a\u0441\u0441\u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u043e\u0432         selector = cssselectorParser.parse(selector);         let res;         \/\/\u043f\u0440\u043e\u0445\u043e\u0434\u0438\u043c \u043f\u043e \u0434\u0435\u0440\u0435\u0432\u0443         for (let i = 0; i &lt;= selector.length - 1; i++) {             let currentSelector = selector[i];             let key;             let item;             \/\/\u0435\u0441\u043b\u0438 \u0442\u0435\u043a\u0443\u0449 \u044d\u043b\u0435\u043c \u0434\u0435\u0440\u0435\u0432\u0430 - \u044d\u0440\u0440\u043e\u0432 - \u043f\u0440\u043e\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0444\u0438\u043b\u044c\u0442\u0440             let isArrowSelector = (currentSelector[0].value === '>');             if (isArrowSelector) {                 continue;             }             \/\/\u043f\u0440\u043e\u0445\u043e\u0434\u0438\u043c \u043f\u043e \u0432\u0441\u0435\u043c \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u043c \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u043a\u0441\u0441\u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u0430             for (var j = 0; j &lt; currentSelector.length; j++) {                 key = currentSelector[j].key                 item = currentSelector[j].value;                 \/\/\u0444\u0438\u043b\u044c\u0442\u0440\u0443\u0435\u043c \u0442\u0435\u043a\u0443\u0449\u0435\u0435 this._tree \u043f\u043e \u0430\u0442\u0442\u0440\u0438\u0431\u0443\u0442\u0430\u043c                 this._filtredByAttribute(key, item)                 }             }             res = this._tree;             \/\/\u0435\u0441\u043b\u0438 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u044d\u043b\u0435\u043c\u0435\u043d\u0442 - \u044d\u0440\u0440\u043e\u0432 - \u0441\u0440\u0435\u0437\u0430\u0435\u043c \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0435\u0440\u0432\u044b\u0439 \u0441\u043b\u043e\u0439, \u0435\u0441\u043b\u0438 \u043d\u0435\u0442 - \u0432\u0441\u0435\u0445 \u0434\u0435\u0442\u0435\u0439             let nextSelectorArrow = selector[i + 1] &amp;&amp; selector[i + 1][0] &amp;&amp; selector[i + 1][0].value === '>';             this._sliceChildrens(nextSelectorArrow)         }         return res;     }<\/code><\/pre>\n<p>\/\/<\/p>\n<p>\u042d\u0442\u0438 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0442 \u043e\u0441\u043d\u043e\u0432\u043d\u0443\u044e \u0440\u0430\u0431\u043e\u0442\u0443, \u0442\u0435\u043f\u0435\u0440\u044c \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0432\u0445\u043e\u0434\u043d\u0443\u044e \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c documentServer.<\/p>\n<pre><code class=\"javascript\">class documentServer {       builderDOM = new BuilderDOM();     domTreeWorker;     startNode;     querySelector(selector) {         this.domTreeWorker.setCurrentTreeByNode(this.startNode);         return this.domTreeWorker.filtredBySelector(selector);     }       build(str) {         this.domTreeWorker = new treeWorker();         global.treeworker = this.domTreeWorker;         let dom = this.builderDOM.html_to_dom(str);         global.treeworker = null;         this.startNode = dom[0];     } }<\/code><\/pre>\n<p>\u041e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0444\u0438\u0447\u0443 \u2014 \u043a\u0432\u0435\u0440\u0438\u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440 \u0438\u0437 \u043d\u043e\u0434\u044b, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u0440\u043e\u043a\u0438\u043d\u0435\u043c domTreeWorker \u0432 \u0434\u043e\u043c_\u043d\u043e\u0434\u0443 \u0447\u0435\u0440\u0435\u0437 \u0433\u043b\u043e\u0431\u0430\u043b<\/p>\n<pre><code class=\"javascript\">class dom_node {     childrens = [];     innerTEXT = '';     tag;     treeWorker;       constructor() {         this.treeWorker = global.treeworker;     }       innerHTML = (cliFormat = false) => {         return this.treeWorker.getInnerHTML(this, cliFormat);     };     querySelector = (selector) => {         this.treeWorker.setCurrentTreeByNode(this);         return this.treeWorker.filtredBySelector(selector);     } } <\/code><\/pre>\n<p><a href=\"https:\/\/github.com\/ru51a4\/server-queryselector\" rel=\"noopener noreferrer nofollow\">\u0421\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u0433\u0438\u0442\u0445\u0430\u0431 <\/a><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p> <!----> <!----><\/div>\n<p> <!----> <!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/post\/703010\/\"> https:\/\/habr.com\/ru\/post\/703010\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0418\u0442\u0430\u043a, \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u0441 \u0432\u0435\u0431 \u0441\u0430\u0439\u0442\u0430 \u2014 \u044d\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0432 3 \u0448\u0430\u0433\u0430<\/p>\n<p>1) \u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c html \u0441\u0430\u0439\u0442\u0430 (\u043f\u0440\u043e\u043f\u0443\u0441\u0442\u0438\u043c \u044d\u0442\u043e\u0442 \u0448\u0430\u0433)<\/p>\n<p>2) \u0420\u0430\u0441\u043f\u0430\u0440\u0441\u0438\u0442\u044c html \u0441\u0442\u0440\u043e\u043a\u0443 \u0438 \u0441\u043e\u0437\u0434\u0430\u0442\u044c dom. \u2014 builderdom.js<\/p>\n<p>3) \u041d\u0430\u0439\u0442\u0438 \u043d\u0443\u0436\u043d\u044b\u0435 dom_node \u0438\u0437 dom \u043f\u043e \u043a\u0441\u0441\u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u0430\u043c.<\/p>\n<p>3.1) \u0420\u0430\u0441\u043f\u0430\u0440\u0441\u0438\u0442\u044c \u0441\u0442\u0440\u043e\u043a\u0443 \u043a\u0441\u0441\u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u043e\u0432 \u0438 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0434\u0435\u0440\u0435\u0432\u043e \u0434\u043b\u044f \u043f\u043e\u0438\u0441\u043a\u0430. \u2014 cssselectorparser.js<br \/>3.2) \u041e\u0442\u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u043e\u043c_\u043d\u043e\u0434\u044b \u043f\u043e \u0434\u0435\u0440\u0435\u0432\u0443 \u043a\u0441\u0441\u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u043e\u0432 \u0438 \u043d\u0430\u0439\u0442\u0438 \u043d\u0443\u0436\u043d\u044b\u0435. \u2014 treeworker.js  <\/p>\n<figure class=\"\"><figcaption><\/figcaption><\/figure>\n<p>2) \u041f\u0430\u0440\u0441\u0438\u043c html:  <\/p>\n<p>2.1) \u041d\u0430\u0440\u0435\u0437\u0430\u0435\u043c \u0441\u0442\u0440\u043e\u043a\u0438(\u0432\u044b\u0434\u0435\u043b\u0438\u043b \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442\u00a0<a href=\"https:\/\/href.li\/?https:\/\/github.com\/ru51a4\/superxmlparser74\" rel=\"noopener noreferrer nofollow\">superxmlparser74<\/a>) <br \/>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0441\u0442\u0440\u043e\u043a\u0438, \u043d\u0430\u043a\u0430\u043f\u043b\u0438\u0432\u0430\u0435\u043c \u0432 \u043d\u0438\u0445 \u0442\u043e\u043a\u0435\u043d\u044b \u0438 \u043e\u0431\u0440\u0435\u0437\u0430\u0435\u043c \u043f\u043e \u043c\u0430\u0440\u043a\u0435\u0440\u0430\u043c<\/p>\n<p>\u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0442\u0435\u0433\/innerTEXT \u2014 t, \u0430\u0442\u0442\u0440\u0438\u0431\u0443\u0442\u044b \u0432 \u0432\u0438\u0434\u0435 \u043c\u0430\u0441\u0441\u0438\u0432\u0430 \u2014 attr<\/p>\n<details class=\"spoiler\">\n<summary>\u043a\u043b\u0438\u043a<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">class superxmlparser74 {     static parse(str, cbOpenTag, cbInnerText, cbClosedTag, cbSelfOpenTag = () => {     }) {         let isOpen = false;         let startAttr = false;         let t = ''         let tAttrKey = '';         let tAttrValue = '';         let tAttrStart = false;         let tAttr = '';         let attr = [];         let prevCh = '';         for (let i = 0; i &lt;= str.length - 1; i++) {             \/\/(1)&lt;li (2)class=\"breadcrumb-item-selected text-gray-light breadcrumb-item text-mono h5-mktg\" aria-current=\"GitHub Student Developer Pack\"(3)>GitHub Student Developer Pack(4)&lt;\/li(5)>             \/\/&lt;selfclosing \/>             \/\/comments \/\/ &lt;!-- -->             if (str[i] === '\/' &amp;&amp; str[i + 1] === \"\/\") {                 for (let j = i + 2; j &lt;= str.length - 1; j++) {                     if (str[j] === '\\n') {                         i = j;                         break;                     }                 }                 continue             } else if (str[i] === \"&lt;\") { \/\/1                 \/\/comments &lt;!-- -->                 if (str[i + 1] === '!' &amp;&amp; str[i + 2] === \"-\" &amp;&amp; str[i + 3] === \"-\") {                     for (let j = i + 4; j &lt;= str.length - 1; j++) {                         if (str[j] === '-' &amp;&amp; str[j + 1] === '-' &amp;&amp; str[j + 2] === '>') {                             i = j + 2;                             break;                         }                     }                     continue                 }                 \/\/\/                  if (t.trim() !== '' &amp;&amp; t.trim() !== \"\\n\" &amp;&amp; t.trim() !== \"\\t\") {                     \/\/cut innerTEXT 4                     cbInnerText({                         value: t                     });                     t = '';                 } else if (str[i + 1] !== \"\/\") {                     cbInnerText({                         value: \"\"                     });                 }                 \/\/open tag                 isOpen = true;                 if (str[i + 1] === \"\/\") {                     isOpen = false;                     i = i + 1;                     continue;                 }             } else if (str[i] === '>') {                 \/\/\/closed tag - build 3\/5                 if (isOpen) {                     if (prevCh === \"\/\") {                         cbSelfOpenTag({                             tag: t,                             attr: attr                         })                     } else {                         cbOpenTag({                             tag: t,                             attr: attr,                         })                     }                 } else {                     cbClosedTag({})                 }                 attr = [];                 t = '';                 startAttr = false;                 isOpen = false;             } else {                 \/\/accum str                 if ((!startAttr &amp;&amp; str[i] !== ' ') || !isOpen) {                     t += str[i];                 } else if (startAttr) { \/\/get attr 2                     if (str[i] === '=') {                         tAttrKey = tAttr                         tAttr = '';                     } else if (str[i] === '\"') {                         tAttrStart = !tAttrStart;                         if (tAttrStart === false) {                             if (tAttrKey === 'class') {                                 tAttrValue = tAttr.split(\" \");                             } else {                                 tAttrValue = [tAttr];                             }                             tAttr = '';                             attr.push({key: tAttrKey, value: tAttrValue});                             if (str[i + 1] === ' ') {                                 i = i + 1;                                 continue;                             }                         }                     } else {                         tAttr += str[i];                     }                  } else if (str[i] === ' ' &amp;&amp; isOpen) {                     startAttr = true;                 }              }             prevCh = str[i];         }     } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>2.2) \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0434\u0435\u0440\u0435\u0432\u043e<\/p>\n<pre><code class=\"javascript\">const superxmlparser74 = require(\"superxmlparser74\");  class dom_node {     childrens = [];     innerTEXT = '';     tag;     treeWorker;      constructor() {         this.treeWorker = global.treeworker;     }      innerHTML = (cliFormat = false) => {         return this.treeWorker.getInnerHTML(this, cliFormat);     };     querySelector = (selector) => {         this.treeWorker.setCurrentTreeByNode(this);         return this.treeWorker.filtredBySelector(selector);     } }  class BuilderDOM {     html_to_dom(str) {         var utils = {             noEndTag(tag) {                 let noEndTags = [                     'noscript',                     'link',                     'base',                     'meta',                     'input',                     'svg',                     'path',                     'img',                     'br',                     'area',                     'base',                     'br',                     'col',                     'embed',                     'hr',                     'img',                     'input',                     'keygen',                     'link',                     'meta',                     'param',                     'source',                     'track',                     'wbr'                 ];                 return noEndTags.includes(tag);             }         };          let res = [];         let parentStack = [];         superxmlparser74.parse(str,             (item) => {                 \/\/opentag                 if (item.tag === 'p' &amp;&amp; parentStack[parentStack.length - 1]?.tag === 'p') {                     parentStack.pop();                 }                 \/\/                 let el = new dom_node();                 el.attr = item.attr;                 el.tag = item.tag;                 res.push(el);                 el.attr.push({                     key: 'tag',                     value: [item.tag]                 })                 if (parentStack[parentStack.length - 1] &amp;&amp; el.tag !== 'script') {                     parentStack[parentStack.length - 1].childrens.push(el)                 }                 if (!utils.noEndTag(el.tag)) {                     parentStack.push(el);                 }             },             (item) => {                 \/\/innertext                 if (parentStack[parentStack.length - 1]) {                     parentStack[parentStack.length - 1].innerTEXT += item.value;                 }             },             (item) => {                 \/\/closedtag                 parentStack.pop();             });          return res;     }  }<\/code><\/pre>\n<p>3) \u041f\u043e\u0438\u0441\u043a<\/p>\n<p>3.1) \u041f\u0430\u0440\u0441\u0438\u043d\u0433 \u043a\u0441\u0441\u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u043e\u0432  <\/p>\n<p>\u0420\u0430\u0437\u0431\u0438\u0432\u0430\u0435\u043c \u0441\u0442\u0440\u043e\u043a\u0443 \u043a\u0441\u0441\u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u043e\u0432 \u043f\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044f\u043c, \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u043a\u0430\u043a\u043e\u0439 \u044d\u0442\u043e \u043a\u0441\u0441\u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440, \u043e\u0431\u0440\u0435\u0437\u0430\u0435\u043c \u0438 \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0434\u0435\u0440\u0435\u0432\u043e.  <\/p>\n<pre><code class=\"javascript\">class cssSelectorParser {     parse(str) {         let res = [];         str = this.utils.lex(str);         for (var i = 0; i &lt;= str.length - 1; i++) {             if (str[i].includes(\".\")) {                 res.push({key: 'class', value: str[i].substring(1)});             } else if (str[i].includes(\"#\")) {                 res.push({key: 'id', value: str[i].substring(1)});             } else if (str[i].includes(\"[\")) {                 let current = str[i];                 current = current.substring(1);                 current = current.slice(0, -1);                 current = current.split(\"=\");                 res.push({key: current[0], value: current[1]});             } else if (str[i] === '>') {                 res.push({key: '', value: str[i]});             } else if (str[i] === ' ') {                 res.push({key: '', value: str[i]});             } else if(str[i] !== '') {                 res.push({key: 'tag', value: str[i]});             }         }         \/\/merge         let mergeRes = [];         let t = [];         for (var i = 0; i &lt;= res.length - 1; i++) {             if (res[i].value === ' ') {                 mergeRes.push(t);                 t = [];             } else {                 t.push(res[i]);             }         }         mergeRes.push(t);         \/\/         return mergeRes;     }       utils = {         lex(str) {             let res = '';             for (var i = 0; i &lt;= str.length - 1; i++) {                 res += str[i];                 if (str[i + 1] === \".\" || str[i + 1] === '#' || str[i + 1] === '>' || str[i + 1] === '[' || (str[i] === ' ')) {                     res += \"\\n\";                 } else if (str[i + 1] === \" \") {                     res += \"\\n\"                 }             }             return res.split(\"\\n\");         }     } }<\/code><\/pre>\n<p>3.2) \u0422\u0435\u043f\u0435\u0440\u044c \u043e\u0442\u0444\u0438\u043b\u044c\u0442\u0440\u0443\u0435\u043c \u0434\u043e\u043c_\u043d\u043e\u0434\u044b \u043f\u043e \u043a\u0441\u0441\u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u0430\u043c  <\/p>\n<pre><code class=\"javascript\">class treeWorker {     \/\/\u0422\u0435\u043a\u0443\u0449\u0438\u0439 \u043c\u0430\u0441\u0441\u0438\u0432 \u0434\u043e\u043c_\u043d\u043e\u0434\u0435     _tree;       \/\/\u041f\u043e\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043c\u0430\u0441\u0441\u0438\u0432 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0432\u0441\u0435\u0445 \u0434\u0435\u0442\u0435\u0439 \u043d\u043e\u0434\u044b     setCurrentTreeByNode(node) {         let tree = this._getChildrens([node]);         this._tree = tree;     }     \/\/\u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0446\u0438\u043a\u043b, \u0433\u0434\u0435 \u043c\u044b \u0438 \u0444\u0438\u043b\u044c\u0442\u0440\u0443\u0435\u043c dom \u043f\u043e \u0434\u0435\u0440\u0435\u0432\u0443 \u043a\u0441\u0441\u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u043e\u0432     filtredBySelector(selector) {         let cssselectorParser = new cssSelectorParser();         selector = cssselectorParser.parse(selector);         let res;         for (let i = 0; i &lt;= selector.length - 1; i++) {             let currentSelector = selector[i];             let key;             let item;             let isArrowSelector = (currentSelector[0].value === '>');             if (isArrowSelector) {       <\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-342050","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/342050","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=342050"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/342050\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=342050"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=342050"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=342050"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}