utiller 1.0.462 → 1.0.463
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/lib/spider/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var _interopRequireDefault=require("@babel/runtime/helpers/interopRequireDefault");Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=void 0;var _defineProperty2=_interopRequireDefault(require("@babel/runtime/helpers/defineProperty")),_index=require("../index.js"),_configerer=require("configerer"),_lodash=_interopRequireDefault(require("lodash"));class Spider{constructor(e,{visible:t=!1,host:o=""}){(0,_defineProperty2.default)(this,"browser",null),(0,_defineProperty2.default)(this,"puppeteer",void 0),(0,_defineProperty2.default)(this,"visible",!0),(0,_defineProperty2.default)(this,"establishBrowserCore",async()=>{const e=await this.puppeteer.launch({headless:!this.visible});for(const t of await e.pages())await t.close();return e}),(0,_defineProperty2.default)(this,"getCurrentBrowser",()=>this.browser),(0,_defineProperty2.default)(this,"initial",async()=>{this.browser=await this.establishBrowserCore()}),(0,_defineProperty2.default)(this,"getPuTeerPage",async({browser:e=this.browser,type:t="desktop",incognito:o=!1,href:i="",timeout:a=0,cookies:r})=>{let n;if(o){const t=await e.createBrowserContext();n=await t.newPage()}else n=await e.newPage();return await this.randomViewport({page:n,type:t}),!_index.utiller.isUndefinedNullEmpty(i)&&_lodash.default.size(r)>0&&(await n.goto(i,{waitUntil:"domcontentloaded"}),await n.setCookie(...r)),_index.utiller.isUndefinedNullEmpty(i)||await n.goto(i,{waitUntil:"networkidle2",timeout:a}),n}),(0,_defineProperty2.default)(this,"randomViewport",async({page:e,type:t="desktop"})=>{const o=_index.utiller.getRandomValue(1080,1920),i=_index.utiller.getRandomValue(1680,1920);await e.setViewport({width:o,height:i})}),(0,_defineProperty2.default)(this,"getPageOfSilent",async({browser:e=this.browser,type:t="desktop",timeout:o=0,cookies:i=[]})=>this.getPuTeerPage({browser:e,type:t,timeout:o,cookies:i})),(0,_defineProperty2.default)(this,"activatePage4Load",async({browser:e=this.browser,href:t="",type:o="desktop",timeout:i=0,incognito:a=!1,cookies:r=[]})=>this.getPuTeerPage({browser:e,href:t,type:o,timeout:i,incognito:a,cookies:r})),(0,_defineProperty2.default)(this,"activatePage4Task",async({browser:e=this.browser,href:t="",type:o="desktop",timeout:i=0,fetcher:a=async e=>!0,incognito:r=!1,cookies:n=[]})=>{const s=await this.activatePage4Load({browser:e,href:t,type:o,timeout:i,incognito:r,cookies:n}),l=await a(s);return await this.close(s),l}),(0,_defineProperty2.default)(this,"auto",async({page:e,incognito:t=!1,href:o,timeout:i=0})=>this.isPuTeerPage(e)?(_index.utiller.isUndefinedNullEmpty(o)||await e.goto(o,{waitUntil:"networkidle2",timeout:i}),await this.randomViewport(e),e):await this.activatePage4Load({incognito:t,timeout:i,href:o})),(0,_defineProperty2.default)(this,"isPuTeerPage",e=>e?.focus),(0,_defineProperty2.default)(this,"clickNextPageTilEnd",async({page:e,selector:t=".pagination-container .pagination > *",sign:o="»"})=>{try{const i=await e.$$(t);if(!i||0===i.length)return console.log("⚠️ 找不到頁面按鈕們"),!1;console.log(`📍 找到 ${i.length} 個頁面按鈕`);let a=null;for(const e of[...i].reverse())if(await this.fetchAttributeOfEl(e,await this.isElementTagBy(e,"a")?"":"a","innerText")===o){a=e;break}return a?(console.log("✅ 找到下一頁按鈕"),await a.evaluate(e=>e.disabled||e.classList.contains("disabled"))?(console.log("⚠️ 下一頁按鈕已禁用 (已是最後一頁)"),!1):(console.log("🖱️ 點擊下一頁按鈕..."),await this.clickNextAndWait4Page(e,a,{selectorOfAnchor:t}),console.log("✅ 成功翻到下一頁"),!0)):(console.log(`⚠️ 找不到下一頁按鈕 (${o})`),!1)}catch(e){return console.log(`❌ 點擊下一頁出錯: ${e.message}`),!1}}),(0,_defineProperty2.default)(this,"getElementSnapshot",async(e,t)=>{try{const o=await e.waitForSelector(t,{timeout:1e3});return await e.evaluate(e=>e.outerHTML,o)}catch(e){return null}}),(0,_defineProperty2.default)(this,"clickNextAndWait4Page",async(e,t,{selectorOfAnchor:o=null,timeoutOfApiWatcher:i=500,timeoutOfNavigation:a=3e4,specificity:r=!1}={})=>{let n=!1,s=null,l=null;const c=r?["xhr","fetch","websocket","eventsource","other"]:["xhr","fetch"],u=t=>{c.includes(t.resourceType())&&(n||(n=!0,s=t.url(),e.off("request",u),console.log(`📡 偵測到 API 請求 (${t.resourceType()}): ${s}`)))};console.log("🖱️ 執行點擊..."),o&&(l=await this.getElementSnapshot(e,o),l?console.log(`📸 成功獲取錨點快照 (${o},長度: ${l.length})`):console.log("⚠️ 無法獲取錨點快照,DOM 變化偵測將被忽略。")),await t.click(),e.on("request",u);try{await Promise.race([new Promise(e=>{let t;const o=setTimeout(()=>{clearInterval(t),n||e("TIMEOUT_NO_API")},i);t=setInterval(()=>{n&&(clearTimeout(o),clearInterval(t),e("API_FIRED"))},50)})])}catch(e){console.warn("Race 過程中發生錯誤:",e.message)}if(e.off("request",u),n&&s){console.log(`✅ 偵測到 API,切換至長等待 (兼容 AJAX/導航/DOM,${a}ms)...`);const t=[e.waitForResponse(e=>e.url()===s&&e.status()>=200&&e.status()<300,{timeout:a}),e.waitForNavigation({waitUntil:"networkidle2",timeout:a}).catch(()=>"NAVIGATION_TIMEOUT")];l&&t.push(e.waitForFunction((e,t)=>{const o=document.querySelector(e);return!o||o.outerHTML!==t},{timeout:a},o,l).catch(e=>{if("TimeoutError"===e.name)return"DOM_CHANGE_TIMEOUT";throw e}));try{await Promise.race(t),console.log("✅ 等待條件滿足(API 響應、頁面導航或 DOM 變化已完成)。")}catch(e){if("TimeoutError"!==e.name)throw e;console.log("⚠️ 長等待超時,API/導航可能發生錯誤或頁面載入極慢。")}}else{if(l){if(await this.getElementSnapshot(e,o)===l)throw console.log("✅ 未偵測到 API/導航,且 DOM 錨點無變化。拋出未變化錯誤。"),new Error("[已到最後一頁]後並未re-render頁面列表(例:...18 -> 19[current]...)");console.log("✅ 未偵測到 API,但 DOM 錨點有變化,等待 DOM 穩定。")}else console.log(`✅ ${i}ms 內未偵測到 API,無快照比對,等待 DOM 穩定。`);await _index.utiller.syncDelay(365)}}),(0,_defineProperty2.default)(this,"wait4Until",async(e,{timeout:t=0}={})=>{await e.waitForNavigation({waitUntil:"networkidle2",timeout:t}).catch(()=>{})}),(0,_defineProperty2.default)(this,"fetchElementsTilPageScrollEnd",async({page:e,href:t="",fetcher:o=async e=>{},stringOfLoadingSelector:i,incognito:a=!1,timeout:r})=>{const n=await this.auto({page:e,incognito:a,href:t,timeout:r});await o(n),await this.scrollToBottomAndCheck(n,{stringOfLoadingSelector:i});const s=await o(n);return await this.close(n),s}),(0,_defineProperty2.default)(this,"fetchAttributesOfEl",async(e,t,o)=>{let i;if(t&&""!==t.trim()){if(i=await e.$(t),!i)return console.warn(`⚠️ 找不到元素: ${t}`),{}}else i=e;const a={},r={};for(const[e,t]of Object.entries(o)){if("object"==typeof t&&null!==t&&!Array.isArray(t)){Object.assign(r,t);continue}const o=String(t);if(o.startsWith("$$$")){const t=o.substring(3),i=parseFloat(t);isNaN(i)?(console.warn(`⚠️ 無法解析數字: ${o}`),r[e]=void 0):r[e]=i;continue}if(o.startsWith("###")){const t=o.substring(3);r[e]=t;continue}if(o.startsWith("@@@")){const t=o.substring(3).toLowerCase().trim();let i;"true"===t||"1"===t||"yes"===t||"y"===t||"on"===t?i=!0:("false"===t||"0"===t||"no"===t||"n"===t||"off"===t||""===t||console.warn(`⚠️ 無法解析布林值: ${o},默認為 false`),i=!1),r[e]=i;continue}a[e]=t}const n=await i.evaluate((e,t)=>{const o={};for(const[i,a]of Object.entries(t)){const t=void 0!==e[a]?e[a]:e.getAttribute(a);o[i]=null!==t?t:void 0}return o},a);return{...n,...r}}),(0,_defineProperty2.default)(this,"fetchAttributeOfEl",async(e,t,o)=>{let i;if(t&&""!==t.trim()){if(i=await e.$(t),!i)return void console.warn(`⚠️ 找不到元素: ${t}`)}else i=e;return await i.evaluate((e,t)=>{const o=void 0!==e[t]?e[t]:e.getAttribute(t);return null!==o?o:void 0},o)}),(0,_defineProperty2.default)(this,"isElementTagBy",async(e,t)=>await e.evaluate((e,t)=>e.tagName.toLowerCase()===t.toLowerCase(),t)),(0,_defineProperty2.default)(this,"waitForLoadingToVanish",async(e,t,o=1e4)=>{if(!t)return!0;try{return await e.waitForSelector(t,{hidden:!0,timeout:o}),!0}catch(e){return console.warn(`警告: Loading Bar ${t} 在 ${o}ms 內未消失,將強制繼續執行。`),!1}}),(0,_defineProperty2.default)(this,"extractBlockTexts",async e=>e?await e.evaluate(e=>{const t=[],o=new Set;return e.querySelectorAll(":scope > *").forEach(e=>{if(["SCRIPT","STYLE","IMG"].includes(e.tagName))return;let i=e.textContent.trim();i.length>0&&(o.has(i)||(t.push(i),o.add(i)))}),t}):[]),(0,_defineProperty2.default)(this,"scrollToBottomAndCheck",async(e,{minDelay:t=2e3,maxRetries:o=3,stringOfLoadingSelector:i,loadingTimeout:a=6e3,fetcher:r}={})=>{console.log("🚀 開始執行智能滾動檢查...");let n=await e.evaluate(()=>document.body.scrollHeight),s=0;for(;;){await e.evaluate(()=>window.scrollTo(0,document.body.scrollHeight)),await new Promise(e=>setTimeout(e,t)),i&&(await this.waitForLoadingToVanish(e,i,a),_lodash.default.isFunction(r)&&await r(e));let l=await e.evaluate(()=>document.body.scrollHeight);if(l===n){if(s++,s>=o){console.log(`✅ 連續 ${o} 次未檢測到新內容,判定已到達最底部。`);break}console.log(`🔄 高度未變化,正在重試等待... (${s}/${o})`),await new Promise(e=>setTimeout(e,1e3))}else console.log(`⬇️ 檢測到新內容 (高度: ${n} -> ${l}),繼續滾動...`),n=l,s=0}}),(0,_defineProperty2.default)(this,"getSelectorRoute",async(e,t)=>await e.evaluate(e=>(e=>{if(e.id)return`#${e.id}`;let t=[];for(;e.parentElement;){let o=e.tagName.toLowerCase();Array.from(e.parentElement.children).filter(t=>t.tagName===e.tagName).length>1&&(o+=`:nth-child(${Array.prototype.indexOf.call(e.parentElement.children,e)+1})`),t.unshift(o),e=e.parentElement}return t.join(" > ")})(e),t)),(0,_defineProperty2.default)(this,"waitSelectorTilAppear",async(e,t,o=1e4)=>{try{return await e.waitForSelector(t,{visible:!0,timeout:o}),console.log(`元素 ${t} 已在 ${e.url()} 上出現!`),!0}catch(e){const i=`元素 ${t} 未在 ${o/1e3} 秒內出現。`;console.error(i,e)}}),(0,_defineProperty2.default)(this,"close",async e=>{if(e)try{const t=e.browserContext();t===e.browser().defaultBrowserContext()?await e.close():await t.close()}catch(e){console.error("關閉頁面/Context 時發生錯誤:",e)}}),(0,_defineProperty2.default)(this,"terminate",async()=>{await this.browser.close()}),this.puppeteer=e,this.visible=t,this.host=o}async fetchElementsTilPageEnd({page:e,href:t="",fetcher:o=async e=>[],selectorOfPagingN:i,signOfPagingN:a="»",incognito:r=!1,timeout:n=0}){const s=await this.auto({page:e,incognito:r,href:t,timeout:n});let l=[];try{do{await _index.utiller.syncDelay(10);const e=await o(s);l=l.concat(e)}while(await this.clickNextPageTilEnd({page:s,selector:i,sign:a,timeout:n}))}catch(e){return console.error(`抓取失敗: ${e.message}`),[]}finally{await this.close(s)}return l}async clearCookies(e){await e.deleteCookie(...await e.cookies())}async checkSelectorExists(e,t){return!!await e.$(t)}async clickSolution(e,t){await e.evaluate(e=>{e.click()},t)}async managePages(e,t=this.browser){const o=e=>{try{const t=new URL(e);return _index.utiller.getUrlPath(this.host,t.pathname)}catch(e){return null}};t.on("targetcreated",async t=>{if("page"===t.type())try{const i=await t.page();if(!i)return;const a=i.url();if("about:blank"===a)return;const r=o(a);r&&r!==e&&(console.log(`[攔截新分頁] 偵測到非法網址: ${r} (目標應為: ${e}),正在關閉...`),await i.close())}catch(e){console.error(`[新分頁檢查錯誤] 無法處理頁面: ${e.message}`)}});try{const i=await t.pages();for(const t of i){const i=t.url();if(t.isClosed()||"about:blank"===i)continue;const a=o(i);a&&a!==e&&(console.log(`[清理舊分頁] 發現非法網址: ${a} (目標應為: ${e}),正在關閉...`),await t.close())}}catch(e){console.error(`[舊分頁清理錯誤] ${e.message}`)}console.log("頁面管理已啟動,保持監聽 10 秒..."),await new Promise(e=>setTimeout(e,1e4))}async checkElementVisibleWithRetry(e,t,o=3e4,i=1e3){const a=Date.now();try{for(;Date.now()-a<o;){if(await e.$(t)){if(await e.evaluate(e=>{const t=document.querySelector(e);if(!t)return!1;const o=window.getComputedStyle(t);return"none"!==o.display&&"hidden"!==o.visibility&&"0"!==o.opacity},t))return console.log(`元素 ${t} 存在且可見`),!0;console.log(`元素 ${t} 存在但不可見,繼續等待...`)}else console.log(`元素 ${t} 不存在,繼續等待...`);await new Promise(e=>setTimeout(e,i))}return console.log(`超時:元素 ${t} 未在 ${o/1e3} 秒內變為可見`),!1}catch(e){return console.error(`發生錯誤: ${e.message}`),!1}}}var _default=exports.default=Spider;
|
|
1
|
+
"use strict";var _interopRequireDefault=require("@babel/runtime/helpers/interopRequireDefault");Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=void 0;var _defineProperty2=_interopRequireDefault(require("@babel/runtime/helpers/defineProperty")),_index=require("../index.js"),_configerer=require("configerer"),_lodash=_interopRequireDefault(require("lodash"));class Spider{constructor(e,{visible:t=!1,host:o=""}){(0,_defineProperty2.default)(this,"browser",null),(0,_defineProperty2.default)(this,"puppeteer",void 0),(0,_defineProperty2.default)(this,"visible",!0),(0,_defineProperty2.default)(this,"establishBrowserCore",async()=>{const e=await this.puppeteer.launch({headless:!this.visible});for(const t of await e.pages())await t.close();return e}),(0,_defineProperty2.default)(this,"getCurrentBrowser",()=>this.browser),(0,_defineProperty2.default)(this,"initial",async()=>{this.browser=await this.establishBrowserCore()}),(0,_defineProperty2.default)(this,"getPuTeerPage",async({browser:e=this.browser,type:t="desktop",incognito:o=!1,href:i="",timeout:a=0,cookies:r})=>{let n;if(o){const t=await e.createBrowserContext();n=await t.newPage()}else n=await e.newPage();return await this.randomViewport({page:n,type:t}),!_index.utiller.isUndefinedNullEmpty(i)&&_lodash.default.size(r)>0&&(await n.goto(i,{waitUntil:"domcontentloaded"}),await n.setCookie(...r)),_index.utiller.isUndefinedNullEmpty(i)||await n.goto(i,{waitUntil:"networkidle2",timeout:a}),n}),(0,_defineProperty2.default)(this,"randomViewport",async({page:e,type:t="desktop"})=>{const o=_index.utiller.getRandomValue(1080,1920),i=_index.utiller.getRandomValue(1680,1920);await e.setViewport({width:o,height:i})}),(0,_defineProperty2.default)(this,"getPageOfSilent",async({browser:e=this.browser,type:t="desktop",timeout:o=0,cookies:i=[]})=>this.getPuTeerPage({browser:e,type:t,timeout:o,cookies:i})),(0,_defineProperty2.default)(this,"activatePage4Load",async({browser:e=this.browser,href:t="",type:o="desktop",timeout:i=0,incognito:a=!1,cookies:r=[]})=>this.getPuTeerPage({browser:e,href:t,type:o,timeout:i,incognito:a,cookies:r})),(0,_defineProperty2.default)(this,"activatePage4Task",async({browser:e=this.browser,href:t="",type:o="desktop",timeout:i=0,fetcher:a=async e=>!0,incognito:r=!1,cookies:n=[]})=>{const s=await this.activatePage4Load({browser:e,href:t,type:o,timeout:i,incognito:r,cookies:n}),l=await a(s);return await this.close(s),l}),(0,_defineProperty2.default)(this,"auto",async({page:e,incognito:t=!1,href:o,timeout:i=0})=>this.isPuTeerPage(e)?(_index.utiller.isUndefinedNullEmpty(o)||await e.goto(o,{waitUntil:"networkidle2",timeout:i}),await this.randomViewport(e),e):await this.activatePage4Load({incognito:t,timeout:i,href:o})),(0,_defineProperty2.default)(this,"isPuTeerPage",e=>e?.focus),(0,_defineProperty2.default)(this,"clickNextPageTilEnd",async({page:e,selector:t=".pagination-container .pagination > *",sign:o="»"})=>{try{const i=await e.$$(t);if(!i||0===i.length)return console.log("⚠️ 找不到頁面按鈕們"),!1;console.log(`📍 找到 ${i.length} 個頁面按鈕`);let a=null;for(const e of[...i].reverse())if(await this.fetchAttributeOfEl(e,await this.isElementTagBy(e,"a")?"":"a","innerText")===o){a=e;break}return a?(console.log("✅ 找到下一頁按鈕"),await a.evaluate(e=>e.disabled||e.classList.contains("disabled"))?(console.log("⚠️ 下一頁按鈕已禁用 (已是最後一頁)"),!1):(console.log("🖱️ 點擊下一頁按鈕..."),await this.clickNextAndWait4Page(e,a,{selectorOfAnchor:t}),console.log("✅ 成功翻到下一頁"),!0)):(console.log(`⚠️ 找不到下一頁按鈕 (${o})`),!1)}catch(e){return console.log(`❌ 點擊下一頁出錯: ${e.message}`),!1}}),(0,_defineProperty2.default)(this,"getElementSnapshot",async(e,t)=>{try{const o=await e.waitForSelector(t,{timeout:1e3});return await e.evaluate(e=>e.outerHTML,o)}catch(e){return null}}),(0,_defineProperty2.default)(this,"clickNextAndWait4Page",async(e,t,{selectorOfAnchor:o=null,timeoutOfApiWatcher:i=500,timeoutOfNavigation:a=3e4,specificity:r=!1}={})=>{let n=!1,s=null,l=null;const c=r?["xhr","fetch","websocket","eventsource","other"]:["xhr","fetch"],u=t=>{c.includes(t.resourceType())&&(n||(n=!0,s=t.url(),e.off("request",u),console.log(`📡 偵測到 API 請求 (${t.resourceType()}): ${s}`)))};console.log("🖱️ 執行點擊..."),o&&(l=await this.getElementSnapshot(e,o),l?console.log(`📸 成功獲取錨點快照 (${o},長度: ${l.length})`):console.log("⚠️ 無法獲取錨點快照,DOM 變化偵測將被忽略。")),await t.click(),e.on("request",u);try{await Promise.race([new Promise(e=>{let t;const o=setTimeout(()=>{clearInterval(t),n||e("TIMEOUT_NO_API")},i);t=setInterval(()=>{n&&(clearTimeout(o),clearInterval(t),e("API_FIRED"))},50)})])}catch(e){console.warn("Race 過程中發生錯誤:",e.message)}if(e.off("request",u),n&&s){console.log(`✅ 偵測到 API,切換至長等待 (兼容 AJAX/導航/DOM,${a}ms)...`);const t=[e.waitForResponse(e=>e.url()===s&&e.status()>=200&&e.status()<300,{timeout:a}),e.waitForNavigation({waitUntil:"networkidle2",timeout:a}).catch(()=>"NAVIGATION_TIMEOUT")];l&&t.push(e.waitForFunction((e,t)=>{const o=document.querySelector(e);return!o||o.outerHTML!==t},{timeout:a},o,l).catch(e=>{if("TimeoutError"===e.name)return"DOM_CHANGE_TIMEOUT";throw e}));try{await Promise.race(t),console.log("✅ 等待條件滿足(API 響應、頁面導航或 DOM 變化已完成)。")}catch(e){if("TimeoutError"!==e.name)throw e;console.log("⚠️ 長等待超時,API/導航可能發生錯誤或頁面載入極慢。")}}else{if(l){if(await this.getElementSnapshot(e,o)===l)throw console.log("✅ 未偵測到 API/導航,且 DOM 錨點無變化。拋出未變化錯誤。"),new Error("[已到最後一頁]後並未re-render頁面列表(例:...18 -> 19[current]...)");console.log("✅ 未偵測到 API,但 DOM 錨點有變化,等待 DOM 穩定。")}else console.log(`✅ ${i}ms 內未偵測到 API,無快照比對,等待 DOM 穩定。`);await _index.utiller.syncDelay(365)}}),(0,_defineProperty2.default)(this,"wait4Until",async(e,{timeout:t=0}={})=>{await e.waitForNavigation({waitUntil:"networkidle2",timeout:t}).catch(()=>{})}),(0,_defineProperty2.default)(this,"fetchElementsTilPageScrollEnd",async({page:e,href:t="",fetcher:o=async e=>{},stringOfLoadingSelector:i,incognito:a=!1,timeout:r})=>{const n=await this.auto({page:e,incognito:a,href:t,timeout:r});await o(n),await this.scrollToBottomAndCheck(n,{stringOfLoadingSelector:i,fetcher:o});const s=await o(n);return await this.close(n),s}),(0,_defineProperty2.default)(this,"fetchAttributesOfEl",async(e,t,o)=>{let i;if(t&&""!==t.trim()){if(i=await e.$(t),!i)return console.warn(`⚠️ 找不到元素: ${t}`),{}}else i=e;const a={},r={};for(const[e,t]of Object.entries(o)){if("object"==typeof t&&null!==t&&!Array.isArray(t)){Object.assign(r,t);continue}const o=String(t);if(o.startsWith("$$$")){const t=o.substring(3),i=parseFloat(t);isNaN(i)?(console.warn(`⚠️ 無法解析數字: ${o}`),r[e]=void 0):r[e]=i;continue}if(o.startsWith("###")){const t=o.substring(3);r[e]=t;continue}if(o.startsWith("@@@")){const t=o.substring(3).toLowerCase().trim();let i;"true"===t||"1"===t||"yes"===t||"y"===t||"on"===t?i=!0:("false"===t||"0"===t||"no"===t||"n"===t||"off"===t||""===t||console.warn(`⚠️ 無法解析布林值: ${o},默認為 false`),i=!1),r[e]=i;continue}a[e]=t}const n=await i.evaluate((e,t)=>{const o={};for(const[i,a]of Object.entries(t)){const t=void 0!==e[a]?e[a]:e.getAttribute(a);o[i]=null!==t?t:void 0}return o},a);return{...n,...r}}),(0,_defineProperty2.default)(this,"fetchAttributeOfEl",async(e,t,o)=>{let i;if(t&&""!==t.trim()){if(i=await e.$(t),!i)return void console.warn(`⚠️ 找不到元素: ${t}`)}else i=e;return await i.evaluate((e,t)=>{const o=void 0!==e[t]?e[t]:e.getAttribute(t);return null!==o?o:void 0},o)}),(0,_defineProperty2.default)(this,"isElementTagBy",async(e,t)=>await e.evaluate((e,t)=>e.tagName.toLowerCase()===t.toLowerCase(),t)),(0,_defineProperty2.default)(this,"waitForLoadingToVanish",async(e,t,o=1e4)=>{if(!t)return!0;try{return await e.waitForSelector(t,{hidden:!0,timeout:o}),!0}catch(e){return console.warn(`警告: Loading Bar ${t} 在 ${o}ms 內未消失,將強制繼續執行。`),!1}}),(0,_defineProperty2.default)(this,"extractBlockTexts",async e=>e?await e.evaluate(e=>{const t=[],o=new Set;return e.querySelectorAll(":scope > *").forEach(e=>{if(["SCRIPT","STYLE","IMG"].includes(e.tagName))return;let i=e.textContent.trim();i.length>0&&(o.has(i)||(t.push(i),o.add(i)))}),t}):[]),(0,_defineProperty2.default)(this,"scrollToBottomAndCheck",async(e,{minDelay:t=2e3,maxRetries:o=3,stringOfLoadingSelector:i,loadingTimeout:a=6e3,fetcher:r}={})=>{console.log("🚀 開始執行智能滾動檢查...");let n=await e.evaluate(()=>document.body.scrollHeight),s=0;for(;;){await e.evaluate(()=>window.scrollTo(0,document.body.scrollHeight)),await new Promise(e=>setTimeout(e,t)),i&&(await this.waitForLoadingToVanish(e,i,a),_lodash.default.isFunction(r)&&await r(e));let l=await e.evaluate(()=>document.body.scrollHeight);if(l===n){if(s++,s>=o){console.log(`✅ 連續 ${o} 次未檢測到新內容,判定已到達最底部。`);break}console.log(`🔄 高度未變化,正在重試等待... (${s}/${o})`),await new Promise(e=>setTimeout(e,1e3))}else console.log(`⬇️ 檢測到新內容 (高度: ${n} -> ${l}),繼續滾動...`),n=l,s=0}}),(0,_defineProperty2.default)(this,"getSelectorRoute",async(e,t)=>await e.evaluate(e=>(e=>{if(e.id)return`#${e.id}`;let t=[];for(;e.parentElement;){let o=e.tagName.toLowerCase();Array.from(e.parentElement.children).filter(t=>t.tagName===e.tagName).length>1&&(o+=`:nth-child(${Array.prototype.indexOf.call(e.parentElement.children,e)+1})`),t.unshift(o),e=e.parentElement}return t.join(" > ")})(e),t)),(0,_defineProperty2.default)(this,"waitSelectorTilAppear",async(e,t,o=1e4)=>{try{return await e.waitForSelector(t,{visible:!0,timeout:o}),console.log(`元素 ${t} 已在 ${e.url()} 上出現!`),!0}catch(e){const i=`元素 ${t} 未在 ${o/1e3} 秒內出現。`;console.error(i,e)}}),(0,_defineProperty2.default)(this,"close",async e=>{if(e)try{const t=e.browserContext();t===e.browser().defaultBrowserContext()?await e.close():await t.close()}catch(e){console.error("關閉頁面/Context 時發生錯誤:",e)}}),(0,_defineProperty2.default)(this,"terminate",async()=>{await this.browser.close()}),this.puppeteer=e,this.visible=t,this.host=o}async fetchElementsTilPageEnd({page:e,href:t="",fetcher:o=async e=>[],selectorOfPagingN:i,signOfPagingN:a="»",incognito:r=!1,timeout:n=0}){const s=await this.auto({page:e,incognito:r,href:t,timeout:n});let l=[];try{do{await _index.utiller.syncDelay(10);const e=await o(s);l=l.concat(e)}while(await this.clickNextPageTilEnd({page:s,selector:i,sign:a,timeout:n}))}catch(e){return console.error(`抓取失敗: ${e.message}`),[]}finally{await this.close(s)}return l}async clearCookies(e){await e.deleteCookie(...await e.cookies())}async checkSelectorExists(e,t){return!!await e.$(t)}async clickSolution(e,t){await e.evaluate(e=>{e.click()},t)}async managePages(e,t=this.browser){const o=e=>{try{const t=new URL(e);return _index.utiller.getUrlPath(this.host,t.pathname)}catch(e){return null}};t.on("targetcreated",async t=>{if("page"===t.type())try{const i=await t.page();if(!i)return;const a=i.url();if("about:blank"===a)return;const r=o(a);r&&r!==e&&(console.log(`[攔截新分頁] 偵測到非法網址: ${r} (目標應為: ${e}),正在關閉...`),await i.close())}catch(e){console.error(`[新分頁檢查錯誤] 無法處理頁面: ${e.message}`)}});try{const i=await t.pages();for(const t of i){const i=t.url();if(t.isClosed()||"about:blank"===i)continue;const a=o(i);a&&a!==e&&(console.log(`[清理舊分頁] 發現非法網址: ${a} (目標應為: ${e}),正在關閉...`),await t.close())}}catch(e){console.error(`[舊分頁清理錯誤] ${e.message}`)}console.log("頁面管理已啟動,保持監聽 10 秒..."),await new Promise(e=>setTimeout(e,1e4))}async checkElementVisibleWithRetry(e,t,o=3e4,i=1e3){const a=Date.now();try{for(;Date.now()-a<o;){if(await e.$(t)){if(await e.evaluate(e=>{const t=document.querySelector(e);if(!t)return!1;const o=window.getComputedStyle(t);return"none"!==o.display&&"hidden"!==o.visibility&&"0"!==o.opacity},t))return console.log(`元素 ${t} 存在且可見`),!0;console.log(`元素 ${t} 存在但不可見,繼續等待...`)}else console.log(`元素 ${t} 不存在,繼續等待...`);await new Promise(e=>setTimeout(e,i))}return console.log(`超時:元素 ${t} 未在 ${o/1e3} 秒內變為可見`),!1}catch(e){return console.error(`發生錯誤: ${e.message}`),!1}}}var _default=exports.default=Spider;
|
package/package.json
CHANGED