我常用的puppeteer爬蟲api

aloha66發表於2018-05-25

本文共1451字,閱讀時間約4分鐘

Puppeteer是Chrome團隊出的nodejs庫(無頭瀏覽器),其中一個功能是網頁抓取(可以作為爬蟲使用)

詳細介紹請看gayhub,更新週期大約是一個月,本文是基於 v1.4.0寫的,大致api是通用的。 本文總結了Puppeteer爬蟲的主要用法。我的目標是,有了這篇文章,日常的一般爬蟲使用就不需要去看官方文件了。

一、安裝和使用

1.1安裝

cnpm i -S puppeteer 用cnpm安裝沒有試過報錯,預設會下載puppeteer配套版本的Chromium。

1.2 使用

const puppeteer = require('puppeteer');

(async () => {
   const browser = await puppeteer.launch({
    headless: false, //預設為true(無頭),不顯示瀏覽器介面
    slowMo :200, //減速顯示,有時會作為模擬人操作特意減速
    devtools: true //顯示開發者工具。頁面寬高預設800*600,把開發者工具顯示再隱藏頁面會佔滿螢幕,有沒有大佬解釋下?
  });
  //生成Page物件
  //const page = await browser.newPage();//官網寫法:一開啟瀏覽器會開啟兩個tab,第二個才是你正在操作的tab
  const page = (await browser.pages())[0]; //這是我的寫法,只有一個tab
  await page.goto('https://www.juejin.com'); //跳轉到掘金
 //請開始你的表演...

  await browser.close(); //關閉瀏覽器
})();
複製程式碼

puppeteer基本上每個操作都會返回一個Promise,記得要用await接收下一步的操作。

puppeteer上也提供了一些第三方寫的爬蟲demo,不過感覺封裝得有點多,先不玩。

二、基本用法

2.1 調整頁面

頁面寬高預設800*600,我覺得太小。我一般都會先初始化頁面大小。

和上面說的一樣,這只是初始化的時候的大小,當開啟再隱藏開發者工具後,頁面就會佔滿全屏了,不知道這是不是bug。

await page.setViewport({
    width: 1280,
    height: 800
  });
複製程式碼

2.2 模擬輸入和點選

目測底層是用document.querySelector()

await page.type(selector, 'Hello puppeteer'); //找到對應的選擇器然後填充值。如果之前設定了slowMo會看到像人打字一樣,值是一個一個填進<input/>
await page.click(selector); //模擬點選,這對傳統非同步分頁(url沒有分頁引數)很有用,selector定在下一頁的標籤上
複製程式碼

2.3 iframe處理

如果網頁內有用iframe等標籤,這時page物件是無法讀取<iframe>裡面的內容的,需要用到page.frames()。返回一個Frame物件陣列。 通常iframe會有name屬性,判斷name屬性可以快速獲取單個Frame物件的內容。

let iframe = await page.frames();
iframe.find(f => f.name() === 'name')
複製程式碼

2.4 waitFor函式

waitFor函式是簡寫,Page和Frame物件都有。我只會用以下兩種方式,剩下了請大佬指點一下。

為了簡化,Page和Frame物件都有的api,我不會再特意說明,會在程式碼中直接體現。

await iframe.waitFor('.contain .item') //在<iframe>中等待'.contain .item'的節點出現,阻塞結束(ps:優先使用,有時200ms我是等不起的)
await page.waitFor(200)//頁面等待200ms
複製程式碼

2.5 selector和emulate

為什麼要合在一點寫呢?因為確實有一個組合的api叫eval

先分開說說吧。

2.5.1 selector

目測底層是用document.querySelector()和document.querySelectorAll()。熟悉這兩個api的人應該很容易上手。

//ps:較少用
page.$(selector) // document.querySelector()
iframe.$$(selector) // document.querySelectorAll(), $$是All的意思
複製程式碼

2.5.2 emulate

這裡首先要有個概念,puppeteer爬蟲解析dom在瀏覽器,這個api的實參在瀏覽器中。所以可以在這個函式內進行dom操作,同時本地的node api是無法在這裡執行的,執行console.log(global)會報錯。

舉個栗子:在函式內有console.log('按f12,我出現在瀏覽器的console中,並不在node命令列')

你會發現node命令列看不到這句話,而在Chromium的console中看見。因此你應該理解他的執行環境是當前網站,而不是你本地的node。

//ps:更少用
await page.evaluate(el => {
    //喜聞樂見的dom操作
})
await iframe.evaluate(el => {
    //請開始你的表演
    console.log('按f12,我出現在瀏覽器的console中,並不在node命令列')
})
複製程式碼

2.5.3 真正的主角 $eval$$eval

上面的兩個api一起用就變成了eval,我最常用的api之一。一個api頂上面兩個,集中一起寫,舒服。

const result = await page.$eval(selector, el => {
    //如果需要賦值要返回Promise
    return new Promise(resolve => {
        //...一波騷操作
        //可以用Dom api啦
        reslove(obj)
    })
});
await iframe.$$eval(selector, el => {...});
複製程式碼

2.6 監聽事件

上文說過在page.evaluate中用console是不能在node命令列列印出來的,不過有了監聽事件就可以改變這個規則了。也可以在監聽事件裡面做容錯處理。

page.on('console', msg => {
    console.log(msg);
});
複製程式碼

個人覺得如果列印dom的話,還是看瀏覽器的console比較好,直觀。

//監聽瀏覽器報錯
page.on('pageerror', pageErr => {
    console.log(pageErr);
});
//監聽node報錯
page.on('error', err => {
    console.log(err);
});
複製程式碼

三、偽裝移動端

const devices = require("puppeteer/DeviceDescriptors");
const iPhone = devices["iPhone 6"];
...
await page.emulate(iPhone);
複製程式碼

更多裝置可以檢視這裡

以上就是我要介紹的puppeteer爬蟲api。


安利兩本書,一本是老姚的正規表示式pdf版說得很詳細,確實很有用,雖然我已經忘了一大半了(╯﹏╰);另一本是 Web 前端面試指南與高頻考題解析小冊子,內容基礎又豐富,希望我可以找個機會實踐一下。

下面我要說說正事

小弟不才,17年軟體工程畢業生,熟悉vue寫過兩個公司專案。目前已離職,座標廣州,求大佬們收留。

相關文章