puppeteer入門實踐

puppeteer初識

puppeteer是一個node庫,他能提供一系列操做Chrome的API,經過這些API咱們能夠用程序代碼來操縱Chrome去完成各類操做。javascript

他運行在node環境,由Chorme官方團隊在維護,既然是操做瀏覽器,那麼咱們能夠手動進行的操做在pupeteer上都能進行。前端

另外pupeteer的英文就是木偶的意思,咱們使用他就像操做木偶玩耍同樣,能夠輕鬆的作到:java

  • 生成頁面 PDF。
  • 抓取頁面並生成預渲染內容。
  • 自動提交表單,進行 UI 測試,鍵盤輸入等。
  • 建立一個時時更新的自動化測試環境。 使用最新的 JavaScript 和瀏覽器功能直接在最新版本的Chrome中執行測試。
  • 捕獲網站的 timeline trace,用來幫助分析性能問題。
  • 測試瀏覽器擴展。
  • ......

固然puppeteer不止能夠作這些,經過在代碼中注入js腳本,咱們幾乎能夠作到全部咱們能夠用代碼實現的功能。node

在puppeteer中,Chrome默認運行在"無頭"模式,而所謂的無頭模式,不過是不加載瀏覽器的UI界面,實際上並不會影響咱們的操做。npm

使用無頭模式,在無界面的狀況下按照咱們編寫的代碼去運行Chrome,能夠減小人爲因素的影響,使運行更加穩定。編程

固然咱們能夠經過他的屬性值headless來控制讓頁面友好的顯示出來,甚至能夠控制頁面顯示多大。json

在前端咱們能夠用它來爬取一些頁面數據進行分析,能夠進行網絡請求的攔截來達到某種業務效果,總之puppeteer很是的好用。網頁爬蟲

既然puppeteer是調用的API來操做的瀏覽器,那麼咱們能夠想到,puppeterr下的瀏覽器,與咱們平時所見到的瀏覽器有什麼不同呢?瀏覽器

又是什麼在吊起了瀏覽器進程,讓咱們可使用代碼與瀏覽器進行通訊的呢?markdown

首先讓咱們瞭解一下puppeteer對瀏覽器的總體架構的分解

  • Browser: 其實對應一個瀏覽器實例,有了這個實例以後咱們就能夠接着去打開一個瀏覽器窗口了。
  • BrowserContext:而一個瀏覽器實例,是能夠打開好幾個窗口的,他們能夠擁有各自的cookie ,session 。
  • Page:表示一個窗口中的一個頁面,固然一個窗口能夠打開不少個頁面。
  • Frame:頁面中的 一個框架,每一個頁面有一個主框架,主要由 iframe 標籤建立產生的
  • ExecutionContext: 是 javascript 的執行環境,每個 Frame 都一個默認的 javascript 執行環境

其次,node庫中的這些API,他們實現的原理實際上是經過Chrome DevTools Protocol(CDP) 協議與瀏覽器進行通訊的。

而node所作的就是將底層經過協議控制的瀏覽器操做,進行簡化封裝,讓咱們能夠簡單的使用。

好比下面是經過CDP來進行頁面的跳轉:

const cdp = new CDP();
await cdp.connect(wsEndpoint);
const targetsResponse = await cdp.send('Target.getTargets');
const pageTarget = targetsResponse.targetInfos.find((t) => t.type === 'page');
const attachResponse = await cdp.send('Target.attachToTarget', { targetId: pageTarget.targetId, flattentrue });
const sessionId = attachResponse.sessionId;
const navigateResponse = await cdp.send('Page.navigate', { url'https://m.jk.cn' }, sessionId);
console.log('navigateResponse', navigateResponse);
複製代碼

而使用puppeteer提供的API的話咱們兩行代碼足以搞定:

const page = await browser.newPage();
await page.goto('https://m.jk.cn');
複製代碼

整體來看對話,他先是將咱們平常用到的瀏覽分紅了各個部分,而後實例化出一個瀏覽器對象,包括瀏覽器上下文,各個頁面等。能夠看到,puppeteer實際上是對CDP操做瀏覽器對代碼進行了簡化,再由node封裝導出API供咱們使用。

再把與瀏覽器通訊對CDP操做進行簡化封裝,最後使用node進行導出API,省去了中間複雜的CDP通訊過程,讓咱們直接使用代碼的方式操做瀏覽器。

puppeteer實踐

咱們使用瀏覽器,大多的操做都是在頁面上進行點擊操做,鍵盤輸入操做等,其實瀏覽器自己提供的還有截圖,保存頁面pdf等功能。

在經過puppeteer對功能的封裝簡化以後,咱們使用幾行代碼即可以完成截圖,保存pdf。

值得一提的是,使用puppeteer幾乎全部操做都是異步的,會返回Promise,咱們可使用.then去處理,可是在node環境下咱們可使用async,await優雅處理

所以咱們的運行環境Nodejs 的版本不能低於 v7.6.0, 須要支持 async,await。

首先在項目目錄下

npm install puppeteer --save
複製代碼

這樣咱們就能夠開始使用puppeteer以編程的方式去操做瀏覽器啦。

const puppeteer = require('puppeteer');
(async () => {
    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    await page.goto('https://www.baidu.com');
    await page.screenshot({path: 'baidu.png'});
    await browser.close();
  })();
複製代碼

上面的這幾行代碼就已經實現了咱們從打開瀏覽器,跳轉頁面,截圖,到關閉瀏覽器的全部過程

先經過 puppeteer.launch() 建立一個瀏覽器實例 Browser 對象 而後經過 Browser 對象建立頁面 Page 對象 而後 page.goto() 跳轉到指定的頁面 調用 page.screenshot() 對頁面進行截圖 關閉瀏覽器 一樣的,若是咱們要獲取頁面的pdf咱們只須要調用他提供的page下的API

await page.pdf({
      path: 'page.pdf',
      printBackground:true,
      format: 'A4'
    });
複製代碼

能夠看到,每一個API均可以傳入參數,這些參數能夠肯定咱們要將獲取到的文件保存在哪path,printBackground是否須要背景圖,以及要什麼樣的尺寸format。

更多的參數屬性能夠參考官方提供的參數說明。

經過puppeteer咱們能夠調用簡單的API完成一些普通的瀏覽器操做,除了這些他還能作的事情其實還不少,咱們能夠模擬用戶實現登陸,或是爬取頁面的數據。

結合入門的基礎操做,這裏以登陸咱們豌豆app爲列作一個模擬登陸的demo,如下爲部分代碼:

(async () => {
  try{
    const browser = await puppeteer.launch({
      headless:false,
      slowMo:300
    });
    const page = await browser.newPage();
    await page.emulate(puppeteer.devices['iPhone 6']);
    await page.goto('https://m.wandougongzhu.cn/user/login');
    await page.tap('.link-btn');
    await page.type('#app > div > div > div:nth-child(3) > input', '12345678901', {delay:300});
    await page.type('#app > div > div > div:nth-child(4) > input', '***********', {delay:300});
    await page.screenshot({ path: 'full.png', fullPage: true });
    await page.tap('#app > div > div > div.btn-box > div');
    await page.waitForTimeout(2000);
    await page.screenshot({ path: 'login.png', fullPage: true });
    await browser.close();
  } catch (error) {
    console.log(error);
  }
})();
複製代碼

爲了方便展現,咱們能夠設置headless爲false這樣就能夠顯示瀏覽器的頁面,接着咱們分析一下代碼

page.emulate調用此方法可讓頁面展現爲一種手機模式 page.tap是對頁面上對DOM元素進行點擊,這裏咱們點擊使用帳號密碼登陸 page.type這個方法能夠獲取頁面上對文本輸入DOM,獲取焦點,並輸入內容,這裏咱們輸入帳號密碼 而後獲取頁面上對登陸按鈕,進行點擊登陸 在使用page.waitForTimeout作一個頁面加載對等待,最後截圖關閉

這樣咱們就實現了以代碼對方式模擬用戶進行登陸操做,而且咱們能夠設置輸入信息對間隔時間,這樣能夠繞過一些頁面對反自動化對規則,保證代碼對穩定性。

基於這種操做咱們能夠實現一些自動化的測試,好比一些表單的提交,按鈕的點擊測試,數據的輸入等等。

經過上面的demo咱們能夠看出,puppeteer的操做大可能是針對與頁面上的DOM元素展開的,只要獲取到頁面上的DOM元素咱們就能夠展開一系列的操做。

如今的一些網頁大多數都是採用js進行後期的渲染加載,使用puppeteer這種獲取到DOM以後在對DOM進行操做對方法,會更穩定一些。

puppeteer 實現爬蟲

puppeteer是根據頁面上的真實存在的DOM進行各類操做的,那麼只要咱們能夠獲取到DOM元素,咱們就能夠獲取到元素在頁面上展現的值。

根據這個思路,咱們就能夠結合js完成一個簡單的網頁爬蟲,經過注入的js代碼,切換頁面,獲取頁面DOM元素,進而拿到數據。

(async () => {
  let data = [];
  const browser = await puppeteer.launch({
    headless: false,
  });
  const page = await browser.newPage();
  for (let mo = 1; mo < 4; mo++) {
    for (let pg = 1; pg <= 10; pg++) {
      mo = mo.toString().padStart(2, "0");
      await page.goto(
        "https://www.bilibili.com/v/music/cover/?spm_id_from=333.5.b_7375626e6176.3#" +
          `/all/click/0/${pg}/2021-${mo}-01,2021-${mo}-28`
      );
      await page.waitForSelector(".vd-list-cnt > ul > li > div > div.r > a");
      let titles = await page.$$eval(".vd-list-cnt > ul > li > div > div.r > a", 
      (links) => links.map((item) => item.innerText)
      );
      console.log(titles);
      data = data.concat(titles);
    }
  }
  fs.writeFile("data.json", JSON.stringify(data, null, "\t"), function (err) {
    if (err) {
      console.log(err);
    }
  });
})();
複製代碼

上面就是結合js進行的簡單的爬蟲,一切咱們在頁面上看到的,均可以使用puppeteer去爬取,而且能夠穩定的爬取到數據,避免一些頁面的渲染都在js中,使用日常的爬蟲很難在拿到數據。

經過這個簡單的爬蟲相信你們對puppeteer的印象又加深了很多,但願你們經過puppeteer與js代碼的結合,能夠完成一些業務上的難題。

相關文章
相關標籤/搜索