亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

詳解Puppeteer 入門(mén)教程

 更新時(shí)間:2018年05月09日 16:52:11   作者:小一輩無(wú)產(chǎn)階級(jí)碼農(nóng)  
本篇文章主要介紹了詳解Puppeteer 入門(mén)教程,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧

1、Puppeteer 簡(jiǎn)介

Puppeteer 是一個(gè)node庫(kù),他提供了一組用來(lái)操縱Chrome的API, 通俗來(lái)說(shuō)就是一個(gè) headless chrome瀏覽器 (當(dāng)然你也可以配置成有UI的,默認(rèn)是沒(méi)有的)。既然是瀏覽器,那么我們手工可以在瀏覽器上做的事情 Puppeteer 都能勝任, 另外,Puppeteer 翻譯成中文是”木偶”意思,所以聽(tīng)名字就知道,操縱起來(lái)很方便,你可以很方便的操縱她去實(shí)現(xiàn):

1) 生成網(wǎng)頁(yè)截圖或者 PDF
2) 高級(jí)爬蟲(chóng),可以爬取大量異步渲染內(nèi)容的網(wǎng)頁(yè)
3) 模擬鍵盤(pán)輸入、表單自動(dòng)提交、登錄網(wǎng)頁(yè)等,實(shí)現(xiàn) UI 自動(dòng)化測(cè)試
4) 捕獲站點(diǎn)的時(shí)間線(xiàn),以便追蹤你的網(wǎng)站,幫助分析網(wǎng)站性能問(wèn)題

如果你用過(guò) PhantomJS 的話(huà),你會(huì)發(fā)現(xiàn)她們有點(diǎn)類(lèi)似,但Puppeteer是Chrome官方團(tuán)隊(duì)進(jìn)行維護(hù)的,用俗話(huà)說(shuō)就是”有娘家的人“,前景更好。

2、運(yùn)行環(huán)境

查看 Puppeteer 的官方 API 你會(huì)發(fā)現(xiàn)滿(mǎn)屏的 async, await 之類(lèi),這些都是 ES7 的規(guī)范,所以你需要:

  1. Nodejs 的版本不能低于 v7.6.0, 需要支持 async, await.
  2. 需要最新的 chrome driver, 這個(gè)你在通過(guò) npm 安裝 Puppeteer 的時(shí)候系統(tǒng)會(huì)自動(dòng)下載的
npm install puppeteer --save

3、基本用法

先開(kāi)看看官方的入門(mén)的 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)頁(yè)截圖,先大概解讀一下上面幾行代碼:

  1. 先通過(guò) puppeteer.launch() 創(chuàng)建一個(gè)瀏覽器實(shí)例 Browser 對(duì)象
  2. 然后通過(guò) Browser 對(duì)象創(chuàng)建頁(yè)面 Page 對(duì)象
  3. 然后 page.goto() 跳轉(zhuǎn)到指定的頁(yè)面
  4. 調(diào)用 page.screenshot() 對(duì)頁(yè)面進(jìn)行截圖
  5. 關(guān)閉瀏覽器

是不是覺(jué)得好簡(jiǎn)單? 反正我是覺(jué)得比 PhantomJS 簡(jiǎn)單,至于跟 selenium-webdriver 比起來(lái), 那更不用說(shuō)了。下面就介紹一下 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)需要特殊說(shuō)明一下,Puppeteer 幾乎所有的操作都是 異步的, 為了使用大量的 then 使得代碼的可讀性降低,本文所有 demo 代碼都是用 async, await 方式實(shí)現(xiàn)。這個(gè) 也是 Puppeteer 官方推薦的寫(xiě)法。對(duì) async/await 一臉懵逼的同學(xué)狠狠的戳這里

options 參數(shù)詳解

參數(shù)名稱(chēng) 參數(shù)類(lèi)型 參數(shù)說(shuō)明
ignoreHTTPSErrors boolean 在請(qǐng)求的過(guò)程中是否忽略 Https 報(bào)錯(cuò)信息,默認(rèn)為 false
headless boolean 是否以”無(wú)頭”的模式運(yùn)行 chrome, 也就是不顯示 UI, 默認(rèn)為 true
executablePath string 可執(zhí)行文件的路勁,Puppeteer 默認(rèn)是使用它自帶的 chrome webdriver, 如果你想指定一個(gè)自己的 webdriver 路徑,可以通過(guò)這個(gè)參數(shù)設(shè)置
slowMo number 使 Puppeteer 操作減速,單位是毫秒。如果你想看看 Puppeteer 的整個(gè)工作過(guò)程,這個(gè)參數(shù)將非常有用。
args Array(String) 傳遞給 chrome 實(shí)例的其他參數(shù),比如你可以使用”–ash-host-window-bounds=1024x768” 來(lái)設(shè)置瀏覽器窗口大小。更多參數(shù)參數(shù)列表可以參考這里
handleSIGINT boolean 是否允許通過(guò)進(jìn)程信號(hào)控制 chrome 進(jìn)程,也就是說(shuō)是否可以使用 CTRL+C 關(guān)閉并退出瀏覽器.
timeout number 等待 Chrome 實(shí)例啟動(dòng)的最長(zhǎng)時(shí)間。默認(rèn)為30000(30秒)。如果傳入 0 的話(huà)則不限制時(shí)間
dumpio boolean 是否將瀏覽器進(jìn)程stdout和stderr導(dǎo)入到process.stdout和process.stderr中。默認(rèn)為false。
userDataDir string 設(shè)置用戶(hù)數(shù)據(jù)目錄,默認(rèn)linux 是在 ~/.config 目錄,window 默認(rèn)在 C:\Users{USER}\AppData\Local\Google\Chrome\User Data, 其中 {USER} 代表當(dāng)前登錄的用戶(hù)名
env Object 指定對(duì)Chromium可見(jiàn)的環(huán)境變量。默認(rèn)為process.env。
devtools boolean 是否為每個(gè)選項(xiàng)卡自動(dòng)打開(kāi)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)斷開(kāi)連接之后重新連接瀏覽器實(shí)例

const puppeteer = require('puppeteer');

puppeteer.launch().then(async browser => {
 // 保存 Endpoint,這樣就可以重新連接 Chromium
 const browserWSEndpoint = browser.wsEndpoint();
 // 從Chromium 斷開(kāi)連接
 browser.disconnect();

 // 使用endpoint 重新和 Chromiunm 建立連接
 const browser2 = await puppeteer.connect({browserWSEndpoint});
 // Close Chromium
 await browser2.close();
});

Browser 對(duì)象 API

方法名稱(chēng) 返回值 說(shuō)明
browser.close() Promise 關(guān)閉瀏覽器
browser.disconnect() void 斷開(kāi)瀏覽器連接
browser.newPage() Promise(Page) 創(chuàng)建一個(gè) Page 實(shí)例
browser.pages() Promise(Array(Page)) 獲取所有打開(kāi)的 Page 實(shí)例
browser.targets() Array(Target) 獲取所有活動(dòng)的 targets
browser.version() Promise(String) 獲取瀏覽器的版本
browser.wsEndpoint() String 返回瀏覽器實(shí)例的 socket 連接 URL, 可以通過(guò)這個(gè) URL 重連接 chrome 實(shí)例

好了,Puppeteer 的API 就不一一介紹了,官方提供的詳細(xì)的 API, 戳這里

4、Puppeteer 實(shí)戰(zhàn)

了解 API 之后我們就可以來(lái)一些實(shí)戰(zhàn)了,在此之前,我們先了解一下 Puppeteer 的設(shè)計(jì)原理,簡(jiǎn)單來(lái)說(shuō) Puppeteer 跟 webdriver 以及 PhantomJS 最大的 的不同就是它是站在用戶(hù)瀏覽的角度,而 webdriver 和 PhantomJS 最初設(shè)計(jì)就是用來(lái)做自動(dòng)化測(cè)試的,所以它是站在機(jī)器瀏覽的角度來(lái)設(shè)計(jì)的,所以它們 使用的是不同的設(shè)計(jì)哲學(xué)。舉個(gè)栗子,加入我需要打開(kāi)京東的首頁(yè)并進(jìn)行一次產(chǎn)品搜索,分別看看使用 Puppeteer 和 webdriver 的實(shí)現(xiàn)流程:

Puppeteer 的實(shí)現(xiàn)流程:

  1. 打開(kāi)京東首頁(yè)
  2. 將光標(biāo) focus 到搜索輸入框
  3. 鍵盤(pán)點(diǎn)擊輸入文字
  4. 點(diǎn)擊搜索按鈕

webdriver 的實(shí)現(xiàn)流程:

  1. 打開(kāi)京東首頁(yè)
  2. 找到輸入框的 input 元素
  3. 設(shè)置 input 的值為要搜索文字
  4. 觸發(fā)搜索按鈕的單機(jī)事件

個(gè)人感覺(jué) Puppeteer 設(shè)計(jì)哲學(xué)更符合任何的操作習(xí)慣,更自然一些。

下面我們就用一個(gè)簡(jiǎn)單的需求實(shí)現(xiàn)來(lái)進(jìn)行 Puppeteer 的入門(mén)學(xué)習(xí)。這個(gè)簡(jiǎn)單的需求就是:

在京東商城抓取10個(gè)手機(jī)商品,并把商品的詳情頁(yè)截圖。

首先我們來(lái)梳理一下操作流程

  1. 打開(kāi)京東首頁(yè)
  2. 輸入“手機(jī)”關(guān)鍵字并搜索
  3. 獲取前10個(gè)商品的 A 標(biāo)簽,并獲取 href 屬性值,獲取商品詳情鏈接
  4. 分別打開(kāi)10個(gè)商品的詳情頁(yè),截取網(wǎng)頁(yè)圖片

要實(shí)現(xiàn)上面的功能需要用到查找元素,獲取屬性,鍵盤(pán)事件等,那接下來(lái)我們就一個(gè)一個(gè)的講解一下。

4.1 獲取元素

Page 對(duì)象提供了2個(gè) API 來(lái)獲取頁(yè)面元素

(1). Page.$(selector) 獲取單個(gè)元素,底層是調(diào)用的是 document.querySelector() , 所以選擇器的 selector 格式遵循css 選擇器規(guī)范

let inputElement = await page.$("#search", input => input);
//下面寫(xiě)法等價(jià)
let inputElement = await page.$('#search');

(2). Page.$$(selector) 獲取一組元素,底層調(diào)用的是 document.querySelectorAll(). 返回 Promise(Array(ElemetHandle)) 元素?cái)?shù)組.

const links = await page.$$("a");
//下面寫(xiě)法等價(jià)
const links = await page.$$("a", links => links);

最終返回的都是 ElemetHandle 對(duì)象

4.2 獲取元素屬性

Puppeteer 獲取元素屬性跟我們平時(shí)寫(xiě)前段的js的邏輯有點(diǎn)不一樣,按照通常的邏輯,應(yīng)該是現(xiàn)獲取元素,然后在獲取元素的屬性。但是上面我們知道 獲取元素的 API 最終返回的都是 ElemetHandle 對(duì)象,而你去查看 ElemetHandle 的 API 你會(huì)發(fā)現(xiàn),它并沒(méi)有獲取元素屬性的 API.

事實(shí)上 Puppeteer 專(zhuān)門(mén)提供了一套獲取屬性的 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 方法,你可以通過(guò)他們來(lái)執(zhí)行一些自定義的 js 代碼,主要提供了下面三個(gè) API

(1). page.evaluate(pageFunction, …args) 返回一個(gè)可序列化的普通對(duì)象,pageFunction 表示要在頁(yè)面執(zhí)行的函數(shù), args 表示傳入給 pageFunction 的參數(shù), 下面的 pageFunction 和 args 表示同樣的意思。

const result = await page.evaluate(() => {
 return Promise.resolve(8 * 7);
});
console.log(result); // prints "56"

這個(gè)方法很有用,比如我們?cè)讷@取頁(yè)面的截圖的時(shí)候,默認(rèn)是只截圖當(dāng)前瀏覽器窗口的尺寸大小,默認(rèn)值是800x600,那如果我們需要獲取整個(gè)網(wǎng)頁(yè)的完整 截圖是沒(méi)辦法辦到的。Page.screenshot() 方法提供了可以設(shè)置截圖區(qū)域大小的參數(shù),那么我們只要在頁(yè)面加載完了之后獲取頁(yè)面的寬度和高度就可以解決 這個(gè)問(wèn)題了。

(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() 方法也是通過(guò) Promise.resolve 方法直接把 Promise 的最終處理結(jié)果返回, 只不過(guò)把最后返回的對(duì)象封裝成了 JSHandle 對(duì)象。本質(zhì)上跟 evaluate 沒(méi)有什么區(qū)別。

下面這段代碼實(shí)現(xiàn)獲取頁(yè)面的動(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), 在文檔頁(yè)面載入前調(diào)用 pageFunction, 如果頁(yè)面中有 iframe 或者 frame, 則函數(shù)調(diào)用 的上下文環(huán)境將變成子頁(yè)面的,即iframe 或者 frame, 由于是在頁(yè)面加載前調(diào)用,這個(gè)函數(shù)一般是用來(lái)初始化 javascript 環(huán)境的,比如重置或者 初始化一些全局變量。

4.4 Page.exposeFunction

除此上面三個(gè) API 之外,還有一類(lèi)似的非常有用的 API, 那就是 Page.exposeFunction,這個(gè) API 用來(lái)在頁(yè)面注冊(cè)全局函數(shù),非常有用:

因?yàn)橛袝r(shí)候需要在頁(yè)面處理一些操作的時(shí)候需要用到一些函數(shù),雖然可以通過(guò) Page.evaluate() API 在頁(yè)面定義函數(shù),比如:

const docSize = await page.evaluate(()=> {
 function getPageSize() {
 return {
 width: document.documentElement.clientWidth,
 height : document.body.clientHeight,
 }
 }

 return getPageSize();
});

但是這樣的函數(shù)不是全局的,需要在每個(gè) evaluate 中去重新定義,無(wú)法做到代碼復(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 使用起來(lái)是很方便的,也非常有用,在比如給 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 修改模擬器(客戶(hù)端)運(yùn)行配置

Puppeteer 提供了一些 API 供我們修改瀏覽器終端的配置

  1. Page.setViewport() 修改瀏覽器視窗大小
  2. Page.setUserAgent() 設(shè)置瀏覽器的 UserAgent 信息
  3. Page.emulateMedia() 更改頁(yè)面的CSS媒體類(lèi)型,用于進(jìn)行模擬媒體仿真。 可選值為 “screen”, “print”, “null”, 如果設(shè)置為 null 則表示禁用媒體仿真。
  4. 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 訪問(wèn)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、鍵盤(pán)和鼠標(biāo)

鍵盤(pán)和鼠標(biāo)的API比較簡(jiǎn)單,鍵盤(pán)的幾個(gè)API如下:

  1. keyboard.down(key[, options]) 觸發(fā) keydown 事件
  2. keyboard.press(key[, options]) 按下某個(gè)鍵,key 表示鍵的名稱(chēng),比如 ‘ArrowLeft' 向左鍵,詳細(xì)的鍵名映射請(qǐng)戳這里
  3. keyboard.sendCharacter(char) 輸入一個(gè)字符
  4. keyboard.type(text, options) 輸入一個(gè)字符串
  5. keyboard.up(key) 觸發(fā) keyup 事件
page.keyboard.press("Shift"); //按下 Shift 鍵
page.keyboard.sendCharacter('嗨');
page.keyboard.type('Hello'); // 一次輸入完成
page.keyboard.type('World', {delay: 100}); // 像用戶(hù)一樣慢慢輸入

鼠標(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 可配置:

  1. options.button 按下了哪個(gè)鍵,可選值為[left, right, middle], 默認(rèn)是 left, 表示鼠標(biāo)左鍵
  2. options.clickCount 按下的次數(shù),單擊,雙擊或者其他次數(shù)
  3. delay 按鍵延時(shí)時(shí)間

mouse.move(x, y, [options]) 移動(dòng)鼠標(biāo)到指定位置, options.steps 表示移動(dòng)的步長(zhǎng)

mouse.up([options]) 觸發(fā) mouseup 事件

7、另外幾個(gè)有用的 API

Puppeteer 還提供幾個(gè)非常有用的 API, 比如:

7.1 Page.waitFor 系列 API

  1. page.waitFor(selectorOrFunctionOrTimeout[, options[, …args]]) 下面三個(gè)的綜合 API
  2. page.waitForFunction(pageFunction[, options[, …args]]) 等待 pageFunction 執(zhí)行完成之后
  3. page.waitForNavigation(options) 等待頁(yè)面基本元素加載完之后,比如同步的 HTML, CSS, JS 等代碼
  4. page.waitForSelector(selector[, options]) 等待某個(gè)選擇器的元素加載之后,這個(gè)元素可以是異步加載的,這個(gè) API 非常有用,你懂的。

比如我想獲取某個(gè)通過(guò) js 異步加載的元素,那么直接獲取肯定是獲取不到的。這個(gè)時(shí)候就可以使用 page.waitForSelector 來(lái)解決:

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()

通過(guò) page.getMetrics() 可以得到一些頁(yè)面性能數(shù)據(jù), 捕獲網(wǎng)站的時(shí)間線(xiàn)跟蹤,以幫助診斷性能問(wèn)題。

  1. Timestamp 度量標(biāo)準(zhǔn)采樣的時(shí)間戳
  2. Documents 頁(yè)面文檔數(shù)
  3. Frames 頁(yè)面 frame 數(shù)
  4. JSEventListeners 頁(yè)面內(nèi)事件監(jiān)聽(tīng)器數(shù)
  5. Nodes 頁(yè)面 DOM 節(jié)點(diǎn)數(shù)
  6. LayoutCount 頁(yè)面布局總數(shù)
  7. RecalcStyleCount 樣式重算數(shù)
  8. LayoutDuration 所有頁(yè)面布局的合并持續(xù)時(shí)間
  9. RecalcStyleDuration 所有頁(yè)面樣式重新計(jì)算的組合持續(xù)時(shí)間。
  10. ScriptDuration 所有腳本執(zhí)行的持續(xù)時(shí)間
  11. TaskDuration 所有瀏覽器任務(wù)時(shí)長(zhǎng)
  12. JSHeapUsedSize JavaScript 占用堆大小
  13. JSHeapTotalSize JavaScript 堆總量

8、總結(jié)和源碼

本文通過(guò)一個(gè)實(shí)際需求來(lái)學(xué)習(xí)了 Puppeteer 的一些基本的常用的 API, API 的版本是 v0.13.0-alpha. 最新邦本的 API 請(qǐng)參考 Puppeteer 官方API.

總的來(lái)說(shuō),Puppeteer 真是一款不錯(cuò)的 headless 工具,操作簡(jiǎn)單,功能強(qiáng)大。用來(lái)做UI自動(dòng)化測(cè)試,和一些小工具都是很不錯(cuò)的。

下面貼上我們開(kāi)始的需求實(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}); //防止頁(yè)面太長(zhǎng),加載超時(shí)

 //注入代碼,慢慢把滾動(dòng)條滑到最底部,保證所有的元素被全部加載
 let scrollEnable = true;
 let scrollStep = 500; //每次滾動(dòng)的步長(zhǎ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一直沒(méi)有解決,發(fā)現(xiàn)截圖的高度最大只能是16384px, 超出部分被截掉了。
 await page.screenshot({path:filename, fullPage:true});
 page.close();
 }

 browser.close();
});

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Node.js從字符串生成文件流的實(shí)現(xiàn)方法

    Node.js從字符串生成文件流的實(shí)現(xiàn)方法

    這篇文章主要介紹了Node.js從字符串生成文件流的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • Node.js API詳解之 tty功能與用法實(shí)例分析

    Node.js API詳解之 tty功能與用法實(shí)例分析

    這篇文章主要介紹了Node.js API詳解之 tty功能與用法,結(jié)合實(shí)例形式分析了Node.js API中tty的基本功能、用法及終端操作相關(guān)使用技巧,需要的朋友可以參考下
    2020-04-04
  • nodejs連接mongodb數(shù)據(jù)庫(kù)實(shí)現(xiàn)增刪改查

    nodejs連接mongodb數(shù)據(jù)庫(kù)實(shí)現(xiàn)增刪改查

    本篇文章主要結(jié)合了nodejs操作mongodb數(shù)據(jù)庫(kù)實(shí)現(xiàn)增刪改查,包括對(duì)數(shù)據(jù)庫(kù)的增加,刪除,查找和更新,有興趣的可以了解一下。
    2016-12-12
  • node-sass@4.14.1報(bào)錯(cuò)的最終解決方案分享

    node-sass@4.14.1報(bào)錯(cuò)的最終解決方案分享

    最近在安裝node-sass@4.14.1的時(shí)候遇到了些問(wèn)題,所以下面這篇文章主要給大家介紹了關(guān)于node-sass@4.14.1報(bào)錯(cuò)的最終解決方案,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-09-09
  • Node.js的HTTP模塊、URL模塊與supervisor工具介紹

    Node.js的HTTP模塊、URL模塊與supervisor工具介紹

    這篇文章介紹了Node.js的HTTP模塊、URL模塊與supervisor工具,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-06-06
  • 關(guān)于Node.js中頻繁修改代碼重啟服務(wù)器的問(wèn)題

    關(guān)于Node.js中頻繁修改代碼重啟服務(wù)器的問(wèn)題

    這篇文章主要介紹了關(guān)于Node.js中頻繁修改代碼重啟服務(wù)器的問(wèn)題,本文給大家分享解決辦法,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-10-10
  • Nodejs異步回調(diào)的優(yōu)雅處理方法

    Nodejs異步回調(diào)的優(yōu)雅處理方法

    這篇文章主要介紹了Nodejs異步回調(diào)的優(yōu)雅處理方法,本文使用了ES6中的新特性,用一種十分優(yōu)雅的方式解決了回調(diào)問(wèn)題,需要的朋友可以參考下
    2014-09-09
  • Nodejs 數(shù)組的隊(duì)列以及forEach的應(yīng)用詳解

    Nodejs 數(shù)組的隊(duì)列以及forEach的應(yīng)用詳解

    這篇文章主要介紹了Nodejs 數(shù)組的隊(duì)列以及forEach的應(yīng)用詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-02-02
  • Egg框架的功能、原理,以及基本使用方法概述

    Egg框架的功能、原理,以及基本使用方法概述

    這篇文章主要介紹了Egg框架的功能、原理,以及基本使用方法,結(jié)合實(shí)例形式較為詳細(xì)的分析了Egg框架的基本功能、原理、使用方法與相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2023-04-04
  • nodejs實(shí)現(xiàn)簡(jiǎn)單的gulp打包

    nodejs實(shí)現(xiàn)簡(jiǎn)單的gulp打包

    因?yàn)橹耙恢庇腥私o我推薦gulp,說(shuō)他這里好哪里好的。實(shí)際上對(duì)我來(lái)說(shuō)夠用就行。grunt熟悉以后實(shí)際上他的配置也不難,說(shuō)到效率的話(huà)確實(shí)是個(gè)問(wèn)題,尤其項(xiàng)目大了以后,目前位置遇到的項(xiàng)目都還可以忍受。不過(guò)不管怎么說(shuō),需要親自用過(guò)gulp之后才能品評(píng)他和grunt之間的優(yōu)劣。
    2017-12-12

最新評(píng)論