fetchWebByPlaywrightHeadless.mjs

import runPlaywright from './runPlaywright.mjs'
import { METHOD_PW_HEADLESS as METHOD } from './constants.mjs'


//預設值
//
//本值與有頭模式之5000不同, 該差異自初版即存在且未見記錄。
//有頭模式另需等待驗證元件出現與人為點擊, 較長之等待有其道理, 但3000與5000這兩個
//具體數字未經量測, 屬未經驗證之推測。調整前應先量測真實頁面之渲染完成時間
let DEFAULT_POST_NAV_WAIT_MS = 3000


/**
 * 使用Playwright無頭Chrome抓取網頁原始HTML
 *
 * 特點:
 * 無頭模式適用SPA等須執行JS渲染之頁面;
 * 可見文字過少時自動穿透Shadow DOM取得內文並重組為簡易HTML;
 * 失敗時自動重試(線性退避3至15秒);
 * 使用playwright之chromium並指定channel='chrome',故執行環境須已安裝Chrome瀏覽器
 *
 * @param {String} url 輸入待抓取網址字串
 * @param {Object} [opt={}] 輸入設定物件,預設{}
 * @param {Integer} [opt.navigationTimeoutMs=15000] 輸入頁面導航最長等待毫秒整數,預設15000
 * @param {Integer} [opt.postNavigationWaitMs=3000] 輸入導航後額外等待毫秒整數,預設3000
 * @param {Boolean} [opt.waitForRedirect=false] 輸入是否等待JS轉址完成布林值,預設false
 * @param {Integer} [opt.maxRetries=5] 輸入失敗時最大重試次數整數,含初始共執行maxRetries+1次,預設5
 * @returns {Promise} 回傳Promise,resolve回傳結果物件,成功時為{status:'success',url,html,htmlLength,contentKind,method,fetchedAt,attempts}(contentKind為'raw'或'synthesized',後者代表內容由Shadow DOM穿透後合成),失敗時為{status:'error',url,message,reason,method,fetchedAt,attempts},本函數不會reject
 * @example
 *
 * import fetchWebByPlaywrightHeadless from './src/fetchWebByPlaywrightHeadless.mjs'
 *
 * let test = async () => {
 *
 *     let r = await fetchWebByPlaywrightHeadless('https://example.com/')
 *     console.log(r.status, r.htmlLength)
 *     // => 'success' 234
 *
 *     let re = await fetchWebByPlaywrightHeadless('abc')
 *     console.log(re.status, re.reason)
 *     // => 'error' 'invalid-url'
 *
 * }
 * await test()
 *     .catch((err) => {
 *         console.log(err)
 *     })
 *
 */
async function fetchWebByPlaywrightHeadless(url, opt = {}) {
    return runPlaywright(url, opt, {
        method: METHOD,
        logName: 'fetchWebByPlaywrightHeadless',
        defaultPostWaitMs: DEFAULT_POST_NAV_WAIT_MS,
        launch: { headless: true, channel: 'chrome' },
    })
}


export default fetchWebByPlaywrightHeadless