【Puppeteer】解決截圖不精確問題
阿新 • • 發佈:2022-05-28
先推一波,歡迎進來指正,提出更好的改進建議 參靠這裡=》最全資料進階
一、起因
使用過 Puppeteer
的小夥伴們一定多多少少接觸過 Puppeteer
裡面的截圖功能,尤其是在一些自動化場景裡,需要涉及驗證碼的自動識別時,必然少不了要將驗證碼圖片擷取下來,然後通過識別介面進行識別。
當我以為一切都是那麼美好的時候,總是會出一些么蛾子。當執行次數達到一定量之後,就會發現很多時候其實截下來的圖片並不是驗證碼圖片這塊區域的圖片內容,而是一張存在著一定的偏差的圖片。這就很傷腦筋。
二、解決過程
為了保證每次截圖都能夠完美擷取,選取了各種不同的擷取範圍,又嘗試了不同的截圖格式,但是最後擷取下來的圖片依然存在著偏移。但是慢慢的我注意到一個現象,每次截圖的時候,介面上所有的東西都會閃一下,然後迅速恢復。如果閃爍時,擷取目標在瀏覽器左上角就是存在偏移的,那麼擷取下來的最終結果就一定也是存在偏移的。
既然這樣,於是我就手動建立一個canvas,然後把目標img繪製到這個canvas上面,這個canvas是一個fixed的並且zindex非長大,一直處於瀏覽器左上角的。然後我截圖擷取這個繪製的好的canvas。沒想到誤打誤撞就可以了,通過這樣一頓操作之後,每次截圖都能夠完美截圖。
三、程式碼
最後整理出這個方法,方便直接進行截圖:
/** * 對指定圖片進行截圖。 * @param page {Page} 頁面 * @param imgSelector {string} img選擇器 * @param type {"png"|"jpeg"} * @return {Promise<string>} 返回擷取成功的base64*/ async function shot(page, imgSelector, type = "png") { // 先建立並繪製canvas。 let canvasId = await page.evaluate(function (select) { let target = document.querySelector(select); if (!target) { throw new Error("未找到選擇器:" + select); } if (target.tagName.toLowerCase() !== "img") {throw new Error("本截圖只支援命中img節點的選擇器,請重新設定選擇器。"); } let id = "ca_" + String(Math.random()).split(".")[1]; let width = target.clientWidth; let height = target.clientHeight; /** * @type {HTMLCanvasElement} */ let canvasElement = document.createElement("canvas"); canvasElement.style.position = "fixed"; canvasElement.style.zIndex = "999999"; canvasElement.style.top = "0"; canvasElement.style.left = "0"; canvasElement.style.width = width + "px"; canvasElement.style.height = height + "px"; canvasElement.id = id; document.body.append(canvasElement); canvasElement.style.display = "block"; canvasElement.width = width; canvasElement.height = height; /** * @type {CanvasRenderingContext2D} */ let context = canvasElement.getContext("2d"); // 繪製白色背景,有些驗證碼可能是透明背景。 context.fillStyle = "#FFFFFF"; context.fillRect(0, 0, width, height); context.drawImage(target, 0, 0, target['naturalWidth'], target['naturalHeight'], 0, 0, width, height); return id; }, imgSelector); /** * 新增並繪製好了之後,再截圖繪製好了的canvas。 * @type {Base64ScreenShotOptions} */ let shotOption = { type: type, encoding: "base64" }; let element = await page.$(`#${canvasId}`); shotOption.clip = await element.boundingBox(); let base64 = await page.screenshot(shotOption); let result = "data:image/" + type + ";base64," + base64.toString(); // 圖截好了,把這個canvas移除。 await page.$eval(`#${canvasId}`, function (element) { element.remove(); }); return result; }
上面的程式碼將生成一個Canvas並且這個canvas層級很高,保證不會被遮擋。
四、總結及注意事項
Puppeteer
截圖是基於瀏覽器左上角的,確切的說,是基於網頁左上角的,當你處理的網頁如果很長,出現了滾動條後,這時截圖一定要先把網頁滾動到最頂部,然後再截圖,要不讓很可能擷取一片白。
更多解決方案 可以參靠這裡=》最全資料進階