Node.js庫Puppeteer經常使用API及騷操做總結

導讀

這篇文章,主要用於收集整理經常使用的Puppeteer的一些經常使用API操做,自動化操做,爬蟲測試,基礎使用等等。固然至因而什麼是Puppeteer呢,咱們來看下官方介紹:Puppeteer是谷歌官方出品的一個經過DevTools協議控制headless ChromeNode庫。能夠經過Puppeteer的提供的api直接控制Chrome模擬大部分用戶操做來進行UI Test或者做爲爬蟲訪問頁面來收集數據。 css

在這裏插入圖片描述

首先備註好中文API文檔地址:點擊打開Puppeteer中文文檔API查看訪問URL地址 官方API文檔地址:github.com/GoogleChrom… 比較不錯的新手文檔資料參考:小一輩無產階級碼農的《Puppeteer 入門教程》html

環境和安裝

cnpm i puppeteer -S
複製代碼

安裝的時候須要注意deshi ,Puppeteer安裝時自帶一個最新版本的Chromium,能夠經過設置環境變量或者npm config中的PUPPETEER_SKIP_CHROMIUM_DOWNLOAD跳過下載。若是不下載的話,啓動時能夠經過puppeteer.launch([options])配置項中的executablePath指定Chromium的位置。git

基本使用

咱們能夠經過操做Browser實例來操做瀏覽器做出對應的事情,好比生成頁面,窗口截圖、生成pdf文件(文字可複製),獲取頁面數據等等,這些都是能夠作到的。github

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('http://rennaiqian.com');
  await page.screenshot({path: 'example.png'});
  await page.pdf({path: 'example.pdf', format: 'A4'});
  await browser.close();
})();
複製代碼

固然,咱們也能夠新增一個headless:true,不打開瀏覽器就能執行咱們的各類操做,默認後臺運行,修改以下:npm

const browser = await puppeteer.launch({headless:false})
複製代碼

new browser.newPage() : 這個方法能夠打開一個新選項卡並返回選項卡的實例page,經過page上的各類方法能夠對頁面進行經常使用操做。上述代碼就進行了截屏和打印pdf的操做。 json

在這裏插入圖片描述
固然咱們最經常使用仍是,動態的植入JS腳本,操做頁面上的元素,而且獲取一些信息,那咱們該如何來實現呢?咱們可使用 page.evaluate(pageFunction, ...args)來實現這一目的,向頁面注入咱們的自定義函數:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('http://rennaiqian.com');

  // Get the "viewport" of the page, as reported by the page.
  const dimensions = await page.evaluate(() => {
    // 在這裏能夠進行DOM操做
    return {
      width: document.documentElement.clientWidth,
      height: document.documentElement.clientHeight,
      deviceScaleFactor: window.devicePixelRatio
    };
  });
  console.log('Dimensions:', dimensions);
  await browser.close();
})();
複製代碼

爬蟲相關實現

固然,這個puppeteer這個框架,用的最多的地方就是在數據爬蟲,頁面模擬點擊操做,用戶名密碼自動輸入,表單提交,cookie無密碼登陸等等這些操做;api

1. 模擬切換設備

這些操做呢,基本上都須要識別當前的設備,因此咱們如何來模擬咱們的當前設備呢?咱們能夠經過page.emulate(mobile)來模擬咱們所須要的設備,以下所示:數組

const puppeteer = require('puppeteer');
const devices = require('puppeteer/DeviceDescriptors'); // puppeteer內置的一些常見設備的模擬參數
const iPhone = devices['iPhone 6'];

puppeteer.launch().then(async browser => {
  const page = await browser.newPage();
  await page.emulate(iPhone);
  await page.goto('https://www.example.com');
  // other actions...
  await browser.close();
});
複製代碼

2. 模擬點擊輸入

使用puppeteer的童鞋大多數都是奔着,自動化測試,使用腳本自動操做頁面內容來着,爬取動態加載網頁;因此呢,模擬點擊輸入整個功能是很是重要,且十分方便的,很是相似於selenium框架,可是比這個框架配置要簡單得多,直接安裝就可使用了,好,接下來咱們就來看看這個點擊輸入又是怎麼完成的呢?瀏覽器

首先咱們須要對點擊輸入分一下類別,分紅 ==鍵盤==和==鼠標==操做,那咱們分別來看下有哪些鍵盤和鼠標操做?bash

2-1. 鍵盤操做

首先咱們列出經常使用的一些API,再羅列出經常使用的一些小案例:

  • keyboard.down(key[, options]) :觸發 keydown 事件
  • keyboard.press(key[, options]) :按下某個鍵,key 表示鍵的名稱,好比 ‘ArrowLeft’ 向左鍵,詳細的鍵名映射* 請戳這裏
  • keyboard.sendCharacter(char) :輸入一個字符
  • keyboard.type(text, options) :輸入一個字符串
  • keyboard.up(key) :觸發 keyup 事件

接下來展現一些經常使用到的一些小案例:

page.keyboard.press("Shift"); //按下 Shift 鍵
page.keyboard.sendCharacter('嗨'); // 輸入一個字符
page.keyboard.type('Hello'); // 一次輸入完成
page.keyboard.type('World', {delay: 100}); // 像用戶同樣慢慢輸入
複製代碼

2-2. 鼠標操做
  • mouse.click(x, y, [options]) :移動鼠標指針到指定的位置,而後按下鼠標,這個其實 mouse.move 和mouse.down 或 mouse.up 的快捷操做
  • mouse.down([options]) :觸發 mousedown 事件,options 可配置:
    • options.button 按下了哪一個鍵,可選值爲 [left, right, middle], 默認是 left, 表示鼠標左鍵
    • options.clickCount 按下的次數,單擊,雙擊或者其餘次數
    • delay 按鍵延時時間
  • mouse.move(x, y, [options]): 移動鼠標到指定位置, options.steps 表示移動的步長
  • mouse.up([options]) :觸發 mouseup 事件

3. 修改瀏覽器運行配置

在咱們爬蟲的過程當中,常常會遇到各式各樣請求頭的問題,比較常常遇到的就是user-agent等等這些參數,因此若是咱們能夠直接修改這些配置的話,就能夠很好的進行爬蟲工做了;並且呢,這個puppeteer同時給咱們提供了一些 API 可讓咱們修改瀏覽器終端的配置:

  • Page.setViewport() :修改瀏覽器視窗大小
  • Page.setUserAgent() :設置瀏覽器的 UserAgent 信息
  • Page.emulateMedia() :更改頁面的CSS媒體類型,用於進行模擬媒體仿真。 可選值爲 「screen」, 「print」, 「null」, 若是設置爲 null 則表示禁用媒體仿真。
  • Page.emulate() :模擬設備,參數設備對象,好比 iPhone, Mac, Android 等,爬H5頁面,親測很好用

再接着,咱們來看一下一些經常使用的小案例:

page.setViewport({width:1920, height:1080}); //設置視窗大小爲 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'); //設置打印機媒體樣式
複製代碼

除此以外咱們還能夠模擬非 PC 機設備, 好比下面這段代碼模擬 iPhone 6 訪問百度:

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.baidu.com');
  // other actions...
  await browser.close();
});
複製代碼

4. 獲取頁面元素

這裏呢,咱們來探討下,如何使用pupeteer來獲取頁面的元素,以及元素裏的內容及屬性;由於這也是使用很是很是頻繁的操做,可是呢,估計你們都比較熟悉,因此呢,把它放在後面進行講解;

4-1. 獲取頁面的元素節點

(1) Page.$(selector) 獲取單個元素,底層是調用的是 document.querySelector(), 因此選擇器的 selector 格式遵循 css 選擇器規範

let inputElement = await page.$("#search", input => input);
//下面寫法等價
let inputElement = await page.$('#search');
複製代碼

(2) Page.$$(selector) 獲取一組元素,底層調用的是 document.querySelectorAll(). 返回 Promise(Array(ElemetHandle))元素數組.

const links = await page.$$("a");
//下面寫法等價
const links = await page.$$("a", links => links);
複製代碼
4-1. 獲取頁面的元素屬性

Puppeteer 獲取元素屬性跟咱們平時寫前段的js的邏輯有點不同,按照一般的邏輯,應該是現獲取元素,而後在獲取元素的屬性。可是上面咱們知道 獲取元素的 API 最終返回的都是 ElemetHandle 對象,而你去查看 ElemetHandle 的 API 你會發現,它並無獲取元素屬性的 API.

Puppeteer 專門提供了一套獲取屬性的 API, Page.$eval()Page.$$eval()

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);
複製代碼

5. 執行自定義的 JS 腳本

執行自定義腳本也是目前來講爬蟲中,比較經常使用的一些功能,經過自定義腳本去拿到接口的簽名等等,都是比較一種很是不錯的爬蟲方式,好,那咱們就一塊兒來看看如何向頁面動態植入自定義腳本呢?

PuppeteerPage 對象提供了一系列 evaluate 方法,你能夠經過他們來執行一些自定義的 js 代碼,主要提供了下面三個 API

5-1. page.evaluate(pageFunction, …args)

這個page.evaluate返回一個可序列化的普通對象,pageFunction 表示要在頁面執行的函數, args 表示傳入給 pageFunction 的參數, 下面的 pageFunction 和 args 表示一樣的意思。

const result = await page.evaluate(() => {
	return Promise.resolve(8 * 7);
});
console.log(result); // 56
複製代碼

5-2. Page.evaluateHandle(pageFunction, …args)

這個evaluateHandle在 Page 上下文執行一個 pageFunction, 返回 JSHandle 實體

const aWindowHandle = await page.evaluateHandle(() => Promise.resolve(window));
aWindowHandle; // Handle for the window object. 

const aHandle = await page.evaluateHandle('document'); // Handle for the 'document'.
複製代碼

下面這段代碼實現獲取頁面的動態(包括js動態插入的元素) 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();
複製代碼

5-3. Page.exposeFunction

Page.exposeFunction,這個 API 用來在頁面註冊全局函數,很是有用: 由於有時候須要在頁面處理一些操做的時候須要用到一些函數,雖然能夠經過 Page.evaluate() API 在頁面定義函數,好比下面代碼實現給 Page 上下文的 window 對象添加 md5 函數:

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 對象註冊 readfile全局函數:

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();
});
複製代碼

6. 等待執行

等待執行相關的 API 主要由 Page.waitFor 所提供

  • page.waitFor(selectorOrFunctionOrTimeout[, options[, …args]]),下面三個的綜合 API
  • page.waitForFunction(pageFunction[, options[, …args]]) ,等待 pageFunction 執行完成以後
  • page.waitForNavigation(options),等待頁面基本元素加載完以後,好比同步的 HTML, CSS, JS 等代碼
  • page.waitForSelector(selector[, options]) ,等待某個選擇器的元素加載以後,這個元素能夠是異步加載的,這個 API 很是有用,你懂的。

好比我想獲取某個經過 js 異步加載的元素,那麼直接獲取確定是獲取不到的。這個時候就可使用 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
		}
	});
});
複製代碼

7. 自動化網頁性能測試

經過 page.getMetrics() 能夠獲得一些頁面性能數據, 捕獲網站的時間線跟蹤,以幫助診斷性能問題。

  • Timestamp 度量標準採樣的時間戳
  • Documents 頁面文檔數
  • Frames 頁面 frame 數
  • JSEventListeners 頁面內事件監聽器數
  • Nodes 頁面 DOM 節點數
  • LayoutCount 頁面佈局總數
  • RecalcStyleCount 樣式重算數
  • LayoutDuration 全部頁面佈局的合併持續時間
  • RecalcStyleDuration 全部頁面樣式從新計算的組合持續時間。
  • ScriptDuration 全部腳本執行的持續時間
  • TaskDuration 全部瀏覽器任務時長
  • JSHeapUsedSize JavaScript 佔用堆大小
  • JSHeapTotalSize JavaScript 堆總量

總結

總的來講, puppeteer 是一款很是不錯的 headless 工具,操做簡單,功能強大。用來作UI自動化測試,和一些小工具都是很不錯的。

相關文章
相關標籤/搜索