透過Puppeteer使用Node.js操作Headless Chrome

2022/03/04

Puppeteer

是Node.js的Headless Chrome API

透過Node.js可輕易的操作Chrome

可操作跳頁、點選按鈕、上傳檔案、爬資料、頁面截圖等任何我們人可以透過chrome的動作

用途上可用於爬蟲、前端自動化測試、自動操作機器人

甚至可配合其他通訊軟體API像是Slack、Line Notify來發送訊息、截圖

 

安裝

首先要確定系統上有安裝Chrome

接者使用yarn或npm安裝即可

# npm
npm i puppeteer

# yarn
yarn add puppeteer

 

啟動Chrome瀏覽器

常用參數https://hakkatv.dgfactor.com/

  • headless: 是否使用headless模式,若在開發模式希望可以看到畫面,可以先設定為false
  • executablePath: chrome的binary file path
  • ignoreHTTPSErrors: 忽略HTTPS錯誤

 

基本使用範例

這段範例將進行

  1. 開啟Google Chrome
  2. 開啟新分頁
  3. 分頁前往Google首頁
  4. 找到搜尋列並輸入"Puppeteer"
  5. 再按下Enter進行搜尋
const puppeteer = require('puppeteer')
class DemoGoogleSearch {
  constructor() {
    this.start()
  }

  async start() {
    // 開啟chrome
    this.browser = await puppeteer.launch({
      headless: false, // 關閉headless模式, debug用
      executablePath: '/usr/bin/google-chrome', // 設定chrome可執行檔位置
    })

    // 建立新分頁
    this.page = await this.browser.newPage()

    // 前往google網址
    await this.page.goto('https://www.google.com/', {
      waitUntil: 'networkidle0',
    })

    // 找到input attribute為"Google 搜尋"的DOM
    await this.page.waitForSelector(`input[title*="Google 搜尋"]`)
    
    // 對input attribute為"Google 搜尋"的DOM輸入"Puppeteer"
    await this.page.type(`input[title*="Google 搜尋"]`, 'Puppeteer')
    
    // 按下Enter按鈕
    await this.page.keyboard.press('Enter')
  }
}

new DemoGoogleSearch()

 

page.goto

這個是最常用到的功能

用來前往某個網址

且通常會使用waitUntil: 'networkidle0'選項

確認500ms內無任何新的請求才完成這個page.goto async method

詳細可參考官方文件page.goto說明

 

等待DOM出現

通常會需要等到頁面載入某個DOM出現

才能接著執行後續動作

因此可以看到Puppeteer文件有著各種類型的waitFor method

我最常用的則是waitForSelector(透過document.querySelectorAll選擇DOM)

或是waitForXPath(透過XPath來選擇DOM)

 

取得DOM內的文字

首先透過page.waitFor取得ElementHandle

並透過以下方式取得DOM內的文字(text)

const dom = await this.page.waitForSelector('input[class*="foobar"]')
const textObject = await targetDom.getProperty('textContent')
const text = textObject._remoteObject.value

 

截圖

透過page.screenshot method即可輕鬆接圖並透過path存到指定位置

await this.page.screenshot({
  path: '/tmp/foobar.png',
})

 

其他常用設定

viewPort

透過page.setViewPort method可設定分頁的view port size

當截圖希望模擬指定裝置大小的時候很好用

await this.page.setViewport({
  width: 1600,
  height: 900,
})

 

Chroem語言

若需要分頁開啟時指定語言

可使用此方式來指定

await this.page.evaluateOnNewDocument(() => {
  Object.defineProperty(navigator, 'language', {
    get: () => 'zh-TW'
  });
  Object.defineProperty(navigator, 'languages', {
    get: () => ['zh-TW', 'zh'],
  });
});

 

停用網站的通知請求dialog

有些網站會使用Notification.requestPermission來跳出dialog請求通知權限

若不希望出現此dialog

可使用args參數加入--disable-notifications選項停用notification

 

this.browser = await puppeteer.launch({
  executablePath: '/usr/bin/google-chrome',
  args: ['--disable-notifications']
})