詳解Puppeteer 入門教程
1、Puppeteer 簡介
Puppeteer 是一個(gè)node庫,他提供了一組用來操縱Chrome的API, 通俗來說就是一個(gè) headless chrome瀏覽器 (當(dāng)然你也可以配置成有UI的,默認(rèn)是沒有的)。既然是瀏覽器,那么我們手工可以在瀏覽器上做的事情 Puppeteer 都能勝任, 另外,Puppeteer 翻譯成中文是”木偶”意思,所以聽名字就知道,操縱起來很方便,你可以很方便的操縱她去實(shí)現(xiàn):
1) 生成網(wǎng)頁截圖或者 PDF
2) 高級(jí)爬蟲,可以爬取大量異步渲染內(nèi)容的網(wǎng)頁
3) 模擬鍵盤輸入、表單自動(dòng)提交、登錄網(wǎng)頁等,實(shí)現(xiàn) UI 自動(dòng)化測(cè)試
4) 捕獲站點(diǎn)的時(shí)間線,以便追蹤你的網(wǎng)站,幫助分析網(wǎng)站性能問題
如果你用過 PhantomJS 的話,你會(huì)發(fā)現(xiàn)她們有點(diǎn)類似,但Puppeteer是Chrome官方團(tuán)隊(duì)進(jìn)行維護(hù)的,用俗話說就是”有娘家的人“,前景更好。
2、運(yùn)行環(huán)境
查看 Puppeteer 的官方 API 你會(huì)發(fā)現(xiàn)滿屏的 async, await 之類,這些都是 ES7 的規(guī)范,所以你需要:
- Nodejs 的版本不能低于 v7.6.0, 需要支持 async, await.
- 需要最新的 chrome driver, 這個(gè)你在通過 npm 安裝 Puppeteer 的時(shí)候系統(tǒng)會(huì)自動(dòng)下載的
npm install puppeteer --save
3、基本用法
先開看看官方的入門的 DEMO
const puppeteer = require('puppeteer'); (async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto('https://example.com'); await page.screenshot({path: 'example.png'}); await browser.close(); })();
上面這段代碼就實(shí)現(xiàn)了網(wǎng)頁截圖,先大概解讀一下上面幾行代碼:
- 先通過 puppeteer.launch() 創(chuàng)建一個(gè)瀏覽器實(shí)例 Browser 對(duì)象
- 然后通過 Browser 對(duì)象創(chuàng)建頁面 Page 對(duì)象
- 然后 page.goto() 跳轉(zhuǎn)到指定的頁面
- 調(diào)用 page.screenshot() 對(duì)頁面進(jìn)行截圖
- 關(guān)閉瀏覽器
是不是覺得好簡單? 反正我是覺得比 PhantomJS 簡單,至于跟 selenium-webdriver 比起來, 那更不用說了。下面就介紹一下 puppeteer 的常用的幾個(gè) API。
3.1 puppeteer.launch(options)
使用 puppeteer.launch() 運(yùn)行 puppeteer,它會(huì) return 一個(gè) promise,使用 then 方法獲取 browser 實(shí)例, 當(dāng)然高版本的 的 nodejs 已經(jīng)支持 await 特性了,所以上面的例子使用 await 關(guān)鍵字,這一點(diǎn)需要特殊說明一下,Puppeteer 幾乎所有的操作都是 異步的, 為了使用大量的 then 使得代碼的可讀性降低,本文所有 demo 代碼都是用 async, await 方式實(shí)現(xiàn)。這個(gè) 也是 Puppeteer 官方推薦的寫法。對(duì) async/await 一臉懵逼的同學(xué)狠狠的戳這里
options 參數(shù)詳解
參數(shù)名稱 | 參數(shù)類型 | 參數(shù)說明 |
ignoreHTTPSErrors | boolean | 在請(qǐng)求的過程中是否忽略 Https 報(bào)錯(cuò)信息,默認(rèn)為 false |
headless | boolean | 是否以”無頭”的模式運(yùn)行 chrome, 也就是不顯示 UI, 默認(rèn)為 true |
executablePath | string | 可執(zhí)行文件的路勁,Puppeteer 默認(rèn)是使用它自帶的 chrome webdriver, 如果你想指定一個(gè)自己的 webdriver 路徑,可以通過這個(gè)參數(shù)設(shè)置 |
slowMo | number | 使 Puppeteer 操作減速,單位是毫秒。如果你想看看 Puppeteer 的整個(gè)工作過程,這個(gè)參數(shù)將非常有用。 |
args | Array(String) | 傳遞給 chrome 實(shí)例的其他參數(shù),比如你可以使用”–ash-host-window-bounds=1024x768” 來設(shè)置瀏覽器窗口大小。更多參數(shù)參數(shù)列表可以參考這里 |
handleSIGINT | boolean | 是否允許通過進(jìn)程信號(hào)控制 chrome 進(jìn)程,也就是說是否可以使用 CTRL+C 關(guān)閉并退出瀏覽器. |
timeout | number | 等待 Chrome 實(shí)例啟動(dòng)的最長時(shí)間。默認(rèn)為30000(30秒)。如果傳入 0 的話則不限制時(shí)間 |
dumpio | boolean | 是否將瀏覽器進(jìn)程stdout和stderr導(dǎo)入到process.stdout和process.stderr中。默認(rèn)為false。 |
userDataDir | string | 設(shè)置用戶數(shù)據(jù)目錄,默認(rèn)linux 是在 ~/.config 目錄,window 默認(rèn)在 C:\Users{USER}\AppData\Local\Google\Chrome\User Data, 其中 {USER} 代表當(dāng)前登錄的用戶名 |
env | Object | 指定對(duì)Chromium可見的環(huán)境變量。默認(rèn)為process.env。 |
devtools | boolean | 是否為每個(gè)選項(xiàng)卡自動(dòng)打開DevTools面板, 這個(gè)選項(xiàng)只有當(dāng) headless 設(shè)置為 false 的時(shí)候有效 |
3.2 Browser 對(duì)象
當(dāng) Puppeteer 連接到一個(gè) Chrome 實(shí)例的時(shí)候就會(huì)創(chuàng)建一個(gè) Browser 對(duì)象,有以下兩種方式:
Puppeteer.launch 和 Puppeteer.connect.
下面這個(gè) DEMO 實(shí)現(xiàn)斷開連接之后重新連接瀏覽器實(shí)例
const puppeteer = require('puppeteer'); puppeteer.launch().then(async browser => { // 保存 Endpoint,這樣就可以重新連接 Chromium const browserWSEndpoint = browser.wsEndpoint(); // 從Chromium 斷開連接 browser.disconnect(); // 使用endpoint 重新和 Chromiunm 建立連接 const browser2 = await puppeteer.connect({browserWSEndpoint}); // Close Chromium await browser2.close(); });
Browser 對(duì)象 API
方法名稱 | 返回值 | 說明 |
browser.close() | Promise | 關(guān)閉瀏覽器 |
browser.disconnect() | void | 斷開瀏覽器連接 |
browser.newPage() | Promise(Page) | 創(chuàng)建一個(gè) Page 實(shí)例 |
browser.pages() | Promise(Array(Page)) | 獲取所有打開的 Page 實(shí)例 |
browser.targets() | Array(Target) | 獲取所有活動(dòng)的 targets |
browser.version() | Promise(String) | 獲取瀏覽器的版本 |
browser.wsEndpoint() | String | 返回瀏覽器實(shí)例的 socket 連接 URL, 可以通過這個(gè) URL 重連接 chrome 實(shí)例 |
好了,Puppeteer 的API 就不一一介紹了,官方提供的詳細(xì)的 API, 戳這里
4、Puppeteer 實(shí)戰(zhàn)
了解 API 之后我們就可以來一些實(shí)戰(zhàn)了,在此之前,我們先了解一下 Puppeteer 的設(shè)計(jì)原理,簡單來說 Puppeteer 跟 webdriver 以及 PhantomJS 最大的 的不同就是它是站在用戶瀏覽的角度,而 webdriver 和 PhantomJS 最初設(shè)計(jì)就是用來做自動(dòng)化測(cè)試的,所以它是站在機(jī)器瀏覽的角度來設(shè)計(jì)的,所以它們 使用的是不同的設(shè)計(jì)哲學(xué)。舉個(gè)栗子,加入我需要打開京東的首頁并進(jìn)行一次產(chǎn)品搜索,分別看看使用 Puppeteer 和 webdriver 的實(shí)現(xiàn)流程:
Puppeteer 的實(shí)現(xiàn)流程:
- 打開京東首頁
- 將光標(biāo) focus 到搜索輸入框
- 鍵盤點(diǎn)擊輸入文字
- 點(diǎn)擊搜索按鈕
webdriver 的實(shí)現(xiàn)流程:
- 打開京東首頁
- 找到輸入框的 input 元素
- 設(shè)置 input 的值為要搜索文字
- 觸發(fā)搜索按鈕的單機(jī)事件
個(gè)人感覺 Puppeteer 設(shè)計(jì)哲學(xué)更符合任何的操作習(xí)慣,更自然一些。
下面我們就用一個(gè)簡單的需求實(shí)現(xiàn)來進(jìn)行 Puppeteer 的入門學(xué)習(xí)。這個(gè)簡單的需求就是:
在京東商城抓取10個(gè)手機(jī)商品,并把商品的詳情頁截圖。
首先我們來梳理一下操作流程
- 打開京東首頁
- 輸入“手機(jī)”關(guān)鍵字并搜索
- 獲取前10個(gè)商品的 A 標(biāo)簽,并獲取 href 屬性值,獲取商品詳情鏈接
- 分別打開10個(gè)商品的詳情頁,截取網(wǎng)頁圖片
要實(shí)現(xiàn)上面的功能需要用到查找元素,獲取屬性,鍵盤事件等,那接下來我們就一個(gè)一個(gè)的講解一下。
4.1 獲取元素
Page 對(duì)象提供了2個(gè) API 來獲取頁面元素
(1). Page.$(selector) 獲取單個(gè)元素,底層是調(diào)用的是 document.querySelector() , 所以選擇器的 selector 格式遵循css 選擇器規(guī)范
let inputElement = await page.$("#search", input => input); //下面寫法等價(jià) let inputElement = await page.$('#search');
(2). Page.$$(selector) 獲取一組元素,底層調(diào)用的是 document.querySelectorAll(). 返回 Promise(Array(ElemetHandle)) 元素?cái)?shù)組.
const links = await page.$$("a"); //下面寫法等價(jià) const links = await page.$$("a", links => links);
最終返回的都是 ElemetHandle 對(duì)象
4.2 獲取元素屬性
Puppeteer 獲取元素屬性跟我們平時(shí)寫前段的js的邏輯有點(diǎn)不一樣,按照通常的邏輯,應(yīng)該是現(xiàn)獲取元素,然后在獲取元素的屬性。但是上面我們知道 獲取元素的 API 最終返回的都是 ElemetHandle 對(duì)象,而你去查看 ElemetHandle 的 API 你會(huì)發(fā)現(xiàn),它并沒有獲取元素屬性的 API.
事實(shí)上 Puppeteer 專門提供了一套獲取屬性的 API, Page.$eval() 和 Page.$$eval()
(1). Page.$$eval(selector, pageFunction[, …args]), 獲取單個(gè)元素的屬性,這里的選擇器 selector 跟上面 Page.$(selector) 是一樣的。
const value = await page.$eval('input[name=search]', input => input.value); const href = await page.$eval('#a", ele => ele.href); const content = await page.$eval('.content', ele => ele.outerHTML);
4.3 執(zhí)行自定義的 JS 腳本
Puppeteer 的 Page 對(duì)象提供了一系列 evaluate 方法,你可以通過他們來執(zhí)行一些自定義的 js 代碼,主要提供了下面三個(gè) API
(1). page.evaluate(pageFunction, …args) 返回一個(gè)可序列化的普通對(duì)象,pageFunction 表示要在頁面執(zhí)行的函數(shù), args 表示傳入給 pageFunction 的參數(shù), 下面的 pageFunction 和 args 表示同樣的意思。
const result = await page.evaluate(() => { return Promise.resolve(8 * 7); }); console.log(result); // prints "56"
這個(gè)方法很有用,比如我們?cè)讷@取頁面的截圖的時(shí)候,默認(rèn)是只截圖當(dāng)前瀏覽器窗口的尺寸大小,默認(rèn)值是800x600,那如果我們需要獲取整個(gè)網(wǎng)頁的完整 截圖是沒辦法辦到的。Page.screenshot() 方法提供了可以設(shè)置截圖區(qū)域大小的參數(shù),那么我們只要在頁面加載完了之后獲取頁面的寬度和高度就可以解決 這個(gè)問題了。
(async () => { const browser = await puppeteer.launch({headless:true}); const page = await browser.newPage(); await page.goto('https://jr.dayi35.com'); await page.setViewport({width:1920, height:1080}); const documentSize = await page.evaluate(() => { return { width: document.documentElement.clientWidth, height : document.body.clientHeight, } }) await page.screenshot({path:"example.png", clip : {x:0, y:0, width:1920, height:documentSize.height}}); await browser.close(); })();
(2). Page.evaluateHandle(pageFunction, …args) 在 Page 上下文執(zhí)行一個(gè) pageFunction, 返回 JSHandle 實(shí)體
const aWindowHandle = await page.evaluateHandle(() => Promise.resolve(window)); aWindowHandle; // Handle for the window object. const aHandle = await page.evaluateHandle('document'); // Handle for the 'document'.
從上面的代碼可以看出,page.evaluateHandle() 方法也是通過 Promise.resolve 方法直接把 Promise 的最終處理結(jié)果返回, 只不過把最后返回的對(duì)象封裝成了 JSHandle 對(duì)象。本質(zhì)上跟 evaluate 沒有什么區(qū)別。
下面這段代碼實(shí)現(xiàn)獲取頁面的動(dòng)態(tài)(包括js動(dòng)態(tài)插入的元素) HTML 代碼.
const aHandle = await page.evaluateHandle(() => document.body); const resultHandle = await page.evaluateHandle(body => body.innerHTML, aHandle); console.log(await resultHandle.jsonValue()); await resultHandle.dispose();
(3). page.evaluateOnNewDocument(pageFunction, …args), 在文檔頁面載入前調(diào)用 pageFunction, 如果頁面中有 iframe 或者 frame, 則函數(shù)調(diào)用 的上下文環(huán)境將變成子頁面的,即iframe 或者 frame, 由于是在頁面加載前調(diào)用,這個(gè)函數(shù)一般是用來初始化 javascript 環(huán)境的,比如重置或者 初始化一些全局變量。
4.4 Page.exposeFunction
除此上面三個(gè) API 之外,還有一類似的非常有用的 API, 那就是 Page.exposeFunction,這個(gè) API 用來在頁面注冊(cè)全局函數(shù),非常有用:
因?yàn)橛袝r(shí)候需要在頁面處理一些操作的時(shí)候需要用到一些函數(shù),雖然可以通過 Page.evaluate() API 在頁面定義函數(shù),比如:
const docSize = await page.evaluate(()=> { function getPageSize() { return { width: document.documentElement.clientWidth, height : document.body.clientHeight, } } return getPageSize(); });
但是這樣的函數(shù)不是全局的,需要在每個(gè) evaluate 中去重新定義,無法做到代碼復(fù)用,在一個(gè)就是 nodejs 有很多工具包可以很輕松的實(shí)現(xiàn)很復(fù)雜的功能 比如要實(shí)現(xiàn) md5 加密函數(shù),這個(gè)用純 js 去實(shí)現(xiàn)就不太方便了,而用 nodejs 卻是幾行代碼的事情。
下面代碼實(shí)現(xiàn)給 Page 上下文的 window 對(duì)象添加 md5 函數(shù):
const puppeteer = require('puppeteer'); const crypto = require('crypto'); puppeteer.launch().then(async browser => { const page = await browser.newPage(); page.on('console', msg => console.log(msg.text)); await page.exposeFunction('md5', text => crypto.createHash('md5').update(text).digest('hex') ); await page.evaluate(async () => { // use window.md5 to compute hashes const myString = 'PUPPETEER'; const myHash = await window.md5(myString); console.log(`md5 of ${myString} is ${myHash}`); }); await browser.close(); });
可以看出,Page.exposeFunction API 使用起來是很方便的,也非常有用,在比如給 window 對(duì)象注冊(cè) readfile 全局函數(shù):
const puppeteer = require('puppeteer'); const fs = require('fs'); puppeteer.launch().then(async browser => { const page = await browser.newPage(); page.on('console', msg => console.log(msg.text)); await page.exposeFunction('readfile', async filePath => { return new Promise((resolve, reject) => { fs.readFile(filePath, 'utf8', (err, text) => { if (err) reject(err); else resolve(text); }); }); }); await page.evaluate(async () => { // use window.readfile to read contents of a file const content = await window.readfile('/etc/hosts'); console.log(content); }); await browser.close(); });
5、Page.emulate 修改模擬器(客戶端)運(yùn)行配置
Puppeteer 提供了一些 API 供我們修改瀏覽器終端的配置
- Page.setViewport() 修改瀏覽器視窗大小
- Page.setUserAgent() 設(shè)置瀏覽器的 UserAgent 信息
- Page.emulateMedia() 更改頁面的CSS媒體類型,用于進(jìn)行模擬媒體仿真。 可選值為 “screen”, “print”, “null”, 如果設(shè)置為 null 則表示禁用媒體仿真。
- Page.emulate() 模擬設(shè)備,參數(shù)設(shè)備對(duì)象,比如 iPhone, Mac, Android 等
page.setViewport({width:1920, height:1080}); //設(shè)置視窗大小為 1920x1080 page.setUserAgent('Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36'); page.emulateMedia('print'); //設(shè)置打印機(jī)媒體樣式
除此之外我們還可以模擬非 PC 機(jī)設(shè)備, 比如下面這段代碼模擬 iPhone 6 訪問google:
const puppeteer = require('puppeteer'); const devices = require('puppeteer/DeviceDescriptors'); const iPhone = devices['iPhone 6']; puppeteer.launch().then(async browser => { const page = await browser.newPage(); await page.emulate(iPhone); await page.goto('https://www.google.com'); // other actions... await browser.close(); });
Puppeteer 支持很多設(shè)備模擬仿真,比如Galaxy, iPhone, IPad 等,想要知道詳細(xì)設(shè)備支持,請(qǐng)戳這里 DeviceDescriptors.js.
6、鍵盤和鼠標(biāo)
鍵盤和鼠標(biāo)的API比較簡單,鍵盤的幾個(gè)API如下:
- keyboard.down(key[, options]) 觸發(fā) keydown 事件
- keyboard.press(key[, options]) 按下某個(gè)鍵,key 表示鍵的名稱,比如 ‘ArrowLeft' 向左鍵,詳細(xì)的鍵名映射請(qǐng)戳這里
- keyboard.sendCharacter(char) 輸入一個(gè)字符
- keyboard.type(text, options) 輸入一個(gè)字符串
- keyboard.up(key) 觸發(fā) keyup 事件
page.keyboard.press("Shift"); //按下 Shift 鍵 page.keyboard.sendCharacter('嗨'); page.keyboard.type('Hello'); // 一次輸入完成 page.keyboard.type('World', {delay: 100}); // 像用戶一樣慢慢輸入
鼠標(biāo)操作:
mouse.click(x, y, [options]) 移動(dòng)鼠標(biāo)指針到指定的位置,然后按下鼠標(biāo),這個(gè)其實(shí) mouse.move 和 mouse.down 或 mouse.up 的快捷操作
mouse.down([options]) 觸發(fā) mousedown 事件,options 可配置:
- options.button 按下了哪個(gè)鍵,可選值為[left, right, middle], 默認(rèn)是 left, 表示鼠標(biāo)左鍵
- options.clickCount 按下的次數(shù),單擊,雙擊或者其他次數(shù)
- delay 按鍵延時(shí)時(shí)間
mouse.move(x, y, [options]) 移動(dòng)鼠標(biāo)到指定位置, options.steps 表示移動(dòng)的步長
mouse.up([options]) 觸發(fā) mouseup 事件
7、另外幾個(gè)有用的 API
Puppeteer 還提供幾個(gè)非常有用的 API, 比如:
7.1 Page.waitFor 系列 API
- page.waitFor(selectorOrFunctionOrTimeout[, options[, …args]]) 下面三個(gè)的綜合 API
- page.waitForFunction(pageFunction[, options[, …args]]) 等待 pageFunction 執(zhí)行完成之后
- page.waitForNavigation(options) 等待頁面基本元素加載完之后,比如同步的 HTML, CSS, JS 等代碼
- page.waitForSelector(selector[, options]) 等待某個(gè)選擇器的元素加載之后,這個(gè)元素可以是異步加載的,這個(gè) API 非常有用,你懂的。
比如我想獲取某個(gè)通過 js 異步加載的元素,那么直接獲取肯定是獲取不到的。這個(gè)時(shí)候就可以使用 page.waitForSelector 來解決:
await page.waitForSelector('.gl-item'); //等待元素加載之后,否則獲取不到異步加載的元素 const links = await page.$$eval('.gl-item > .gl-i-wrap > .p-img > a', links => { return links.map(a => { return { href: a.href.trim(), name: a.title } }); });
其實(shí)上面的代碼就可以解決我們最上面的需求,抓取京東的產(chǎn)品,因?yàn)槭钱惒郊虞d的,所以使用這種方式。
7.2 page.getMetrics()
通過 page.getMetrics() 可以得到一些頁面性能數(shù)據(jù), 捕獲網(wǎng)站的時(shí)間線跟蹤,以幫助診斷性能問題。
- Timestamp 度量標(biāo)準(zhǔn)采樣的時(shí)間戳
- Documents 頁面文檔數(shù)
- Frames 頁面 frame 數(shù)
- JSEventListeners 頁面內(nèi)事件監(jiān)聽器數(shù)
- Nodes 頁面 DOM 節(jié)點(diǎn)數(shù)
- LayoutCount 頁面布局總數(shù)
- RecalcStyleCount 樣式重算數(shù)
- LayoutDuration 所有頁面布局的合并持續(xù)時(shí)間
- RecalcStyleDuration 所有頁面樣式重新計(jì)算的組合持續(xù)時(shí)間。
- ScriptDuration 所有腳本執(zhí)行的持續(xù)時(shí)間
- TaskDuration 所有瀏覽器任務(wù)時(shí)長
- JSHeapUsedSize JavaScript 占用堆大小
- JSHeapTotalSize JavaScript 堆總量
8、總結(jié)和源碼
本文通過一個(gè)實(shí)際需求來學(xué)習(xí)了 Puppeteer 的一些基本的常用的 API, API 的版本是 v0.13.0-alpha. 最新邦本的 API 請(qǐng)參考 Puppeteer 官方API.
總的來說,Puppeteer 真是一款不錯(cuò)的 headless 工具,操作簡單,功能強(qiáng)大。用來做UI自動(dòng)化測(cè)試,和一些小工具都是很不錯(cuò)的。
下面貼上我們開始的需求實(shí)現(xiàn)源碼,僅供參考:
//延時(shí)函數(shù) function sleep(delay) { return new Promise((resolve, reject) => { setTimeout(() => { try { resolve(1) } catch (e) { reject(0) } }, delay) }) } const puppeteer = require('puppeteer'); puppeteer.launch({ ignoreHTTPSErrors:true, headless:false,slowMo:250, timeout:0}).then(async browser => { let page = await browser.newPage(); await page.setJavaScriptEnabled(true); await page.goto("https://www.jd.com/"); const searchInput = await page.$("#key"); await searchInput.focus(); //定位到搜索框 await page.keyboard.type("手機(jī)"); const searchBtn = await page.$(".button"); await searchBtn.click(); await page.waitForSelector('.gl-item'); //等待元素加載之后,否則獲取不異步加載的元素 const links = await page.$$eval('.gl-item > .gl-i-wrap > .p-img > a', links => { return links.map(a => { return { href: a.href.trim(), title: a.title } }); }); page.close(); const aTags = links.splice(0, 10); for (var i = 1; i < aTags.length; i++) { page = await browser.newPage() page.setJavaScriptEnabled(true); await page.setViewport({width:1920, height:1080}); var a = aTags[i]; await page.goto(a.href, {timeout:0}); //防止頁面太長,加載超時(shí) //注入代碼,慢慢把滾動(dòng)條滑到最底部,保證所有的元素被全部加載 let scrollEnable = true; let scrollStep = 500; //每次滾動(dòng)的步長 while (scrollEnable) { scrollEnable = await page.evaluate((scrollStep) => { let scrollTop = document.scrollingElement.scrollTop; document.scrollingElement.scrollTop = scrollTop + scrollStep; return document.body.clientHeight > scrollTop + 1080 ? true : false }, scrollStep); await sleep(100); } await page.waitForSelector("#footer-2014", {timeout:0}); //判斷是否到達(dá)底部了 let filename = "images/items-"+i+".png"; //這里有個(gè)Puppeteer的bug一直沒有解決,發(fā)現(xiàn)截圖的高度最大只能是16384px, 超出部分被截掉了。 await page.screenshot({path:filename, fullPage:true}); page.close(); } browser.close(); });
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Node.js從字符串生成文件流的實(shí)現(xiàn)方法
這篇文章主要介紹了Node.js從字符串生成文件流的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08Node.js API詳解之 tty功能與用法實(shí)例分析
這篇文章主要介紹了Node.js API詳解之 tty功能與用法,結(jié)合實(shí)例形式分析了Node.js API中tty的基本功能、用法及終端操作相關(guān)使用技巧,需要的朋友可以參考下2020-04-04nodejs連接mongodb數(shù)據(jù)庫實(shí)現(xiàn)增刪改查
本篇文章主要結(jié)合了nodejs操作mongodb數(shù)據(jù)庫實(shí)現(xiàn)增刪改查,包括對(duì)數(shù)據(jù)庫的增加,刪除,查找和更新,有興趣的可以了解一下。2016-12-12node-sass@4.14.1報(bào)錯(cuò)的最終解決方案分享
最近在安裝node-sass@4.14.1的時(shí)候遇到了些問題,所以下面這篇文章主要給大家介紹了關(guān)于node-sass@4.14.1報(bào)錯(cuò)的最終解決方案,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-09-09Node.js的HTTP模塊、URL模塊與supervisor工具介紹
這篇文章介紹了Node.js的HTTP模塊、URL模塊與supervisor工具,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06關(guān)于Node.js中頻繁修改代碼重啟服務(wù)器的問題
這篇文章主要介紹了關(guān)于Node.js中頻繁修改代碼重啟服務(wù)器的問題,本文給大家分享解決辦法,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-10-10Nodejs 數(shù)組的隊(duì)列以及forEach的應(yīng)用詳解
這篇文章主要介紹了Nodejs 數(shù)組的隊(duì)列以及forEach的應(yīng)用詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2021-02-02