Puppeteer怎么用來做爬蟲

這期內(nèi)容當(dāng)中小編將會給大家?guī)碛嘘P(guān)Puppeteer怎么用來做爬蟲,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

成都網(wǎng)站建設(shè)、網(wǎng)站設(shè)計服務(wù)團(tuán)隊是一支充滿著熱情的團(tuán)隊,執(zhí)著、敏銳、追求更好,是創(chuàng)新互聯(lián)的標(biāo)準(zhǔn)與要求,同時竭誠為客戶提供服務(wù)是我們的理念。創(chuàng)新互聯(lián)把每個網(wǎng)站當(dāng)做一個產(chǎn)品來開發(fā),精雕細(xì)琢,追求一名工匠心中的細(xì)致,我們更用心!

前言

自動化測試對于軟件開發(fā)來說是一個很重要也很方便的東西,但是自動化測試工具除了能用來做測試以外,還能被用來做一些模擬人類操作的事情,所以一些 E2E 自動化測試工具(例如:Selenium、Puppeteer、Appium)因為其強(qiáng)大的模擬功能,經(jīng)常還被爬蟲工程師們用來抓取數(shù)據(jù)。

網(wǎng)上有很多將自動化測試工具作為爬蟲的抓取教程,不過僅僅都限于如何獲取數(shù)據(jù),而我們知道這些基于瀏覽器的解決方案都有較大的性能開銷,而且效率不高,并不是爬蟲的最佳選擇。

本篇文章將介紹自動化測試工具的另一種用法,也就是用來自動化一些人工操作。我們使用的工具是谷歌開發(fā)并開源的測試框架 Puppeteer ,它會操作 Chromium (谷歌開發(fā)的開源瀏覽器)來完成自動化。我們將一步一步介紹如何利用 Puppeteer 在掘金上自動發(fā)布文章。

自動化測試工具的原理

自動化測試工具的原理是通過程式化地操作瀏覽器,與其進(jìn)行模擬交互(例如點(diǎn)擊、打字、導(dǎo)航等等)來控制要抓取的網(wǎng)頁。自動化測試工具通常也能獲取網(wǎng)頁的 DOM 或 HTML,因此也可以輕松的獲取網(wǎng)頁數(shù)據(jù)。

此外,對于一些動態(tài)網(wǎng)站來說,JS 動態(tài)渲染的數(shù)據(jù)通常不能輕松獲取,而自動化測試工具則可以輕松的做到,因為它是將 HTML 輸入瀏覽器里運(yùn)行的。

Puppeteer 簡介

Puppeteer怎么用來做爬蟲

這里摘抄 Puppeteer 的 Github 主頁上的定義(英文)。

Puppeteer is a Node library which provides a high-level API to control Chrome or Chromium over the DevTools Protocol. Puppeteer runs headless by default, but can be configured to run full (non-headless) Chrome or Chromium.

翻譯過來大致是: Puppeteer 是一個 Node.js 庫,提供了高級 API 來控制 Chrome 或 Chromium (通過開發(fā)工具協(xié)議); Puppeteer 默認(rèn)的運(yùn)行模式是無頭的,但是可以被配置成非無頭的模式。

Loco注:無頭指的是不顯示瀏覽器的GUI,是為了提升性能而設(shè)計的,因為渲染圖像是一件很消耗資源的事情。

以下是 Puppeteer 可以做的事情:

  • 生成截圖和頁面 PDF ;

  • 抓取單頁應(yīng)用,產(chǎn)生預(yù)渲染內(nèi)容(即 SSR ,服務(wù)端渲染);

  • 自動化表單提交、 UI 測試、鍵盤輸入等等;

  • 創(chuàng)建一個最新的、自動化的測試環(huán)境;

  • 捕獲網(wǎng)站的時間線來幫助診斷性能問題;

  • 測試 Chrome 插件;

  • ...

Puppeteer 安裝

安裝 Puppeteer 并不難,只需要保證你的環(huán)境上安裝了 Node.js 以及能夠運(yùn)行 NPM。

由于官方的安裝教程沒有考慮到已經(jīng)安裝了 Chromium 的情況,我們這里使用一個第三方庫 puppeteer-chromium-resolver,它能夠自定義化 Puppeteer 以及管理 Chromium 的下載情況。

運(yùn)行以下命令安裝 Puppeteer:

npm install puppeteer-chromium-resolver --save

puppeteer-chromium-resolver 的詳細(xì)用法請參照官網(wǎng):https://www.npmjs.com/package/puppeteer-chromium-resolver。

Puppeteer 常用命令

Puppeteer 的官方API文檔是 https://pptr.dev/ ,文檔里有詳細(xì)的 Puppeteer 的開放接口,可以進(jìn)行參考,這里我們只列出一些常用的接口命令。

生成/關(guān)閉瀏覽器
// 引入puppeteer-chromium-resolver
const PCR = require('puppeteer-chromium-resolver')

// 生成PCR實例
const pcr = await PCR({
    revision: '',
    detectionPath: '',
    folderName: '.chromium-browser-snapshots',
    hosts: ['https://storage.googleapis.com', 'https://npm.taobao.org/mirrors'],
    retry: 3,
    silent: false
})

// 生成瀏覽器
const browser = await pcr.puppeteer.launch({...})

// 關(guān)閉瀏覽器
await browser.close()
生成頁面
const page = await browser.newPage()
導(dǎo)航
await page.goto('https://baidu.com')
等待
await page.waitFor(3000)
await page.goto('https://baidu.com')
獲取頁面元素
const el = await page.$(selector)
點(diǎn)擊元素
await el.click()
輸入內(nèi)容
await el.type(text)
執(zhí)行Console代碼(重點(diǎn))
const res = await page.evaluate((arg1, arg2, arg3) => {
    // anything frontend
    return 'frontend awesome'
}, arg1, arg2, arg3)

這應(yīng)該是 Puppeteer 中最強(qiáng)大的 API 了。任何熟悉前端技術(shù)的開發(fā)者都應(yīng)該了解 Chrome 開發(fā)者工具中的 Console,任何 JS 的代碼都可以在這里被運(yùn)行,其中包括點(diǎn)擊事件、獲取元素、增刪改元素等等。我們的自動發(fā)文程序?qū)⒋罅坑玫竭@個 API 。

可以看到 evaluate 方法可以接受一些參數(shù),并作為回調(diào)函數(shù)中的參數(shù)作用在前端代碼中。這讓我們可以將后端的任何數(shù)據(jù)注入到前端 DOM 中,例如文章標(biāo)題和文章內(nèi)容等等。

另外,回調(diào)函數(shù)中的返回值可以作為 evaluate 的返回值,賦值給 res,這經(jīng)常被用作數(shù)據(jù)抓取。

注意,上面的這些代碼都用了 await 這個關(guān)鍵字,這其實是 ES7 中的 async/await 新語法,是 ES6 的 Promise 的語法糖,讓異步代碼更容易閱讀和理解。如果對 async/await 不理解的同學(xué),可以參考這篇文章:https://juejin.im/post/596e142d5188254b532ce2da。

Puppeteer 實戰(zhàn):在掘金上自動發(fā)布文章

常言說:Talk is cheap, show me the code。

下面,我們將用一個自動發(fā)文章的例子來展示 Puppeteer 的功能。本文中用來作為示例的平臺是掘金。

為什么選擇掘金呢?這是因為掘金的登錄并不像其他某些網(wǎng)站(例如 CSDN )要求輸入驗證碼(這會增大復(fù)雜度),只要求輸入賬戶名和密碼就可以登錄了。

為了方便新手理解,我們將從爬蟲基本結(jié)構(gòu)開始講解。(限于篇幅考慮,我們將略過瀏覽器和頁面的初始化,只挑重點(diǎn)講解)

基礎(chǔ)結(jié)構(gòu)

為了讓爬蟲顯得不那么亂七八糟,我們將發(fā)布文章的各個步驟抽離了出來,形成了一個基類(因為我們可能不止掘金一個平臺要抓取,使用面向?qū)ο蟮乃枷刖帉懘a的話,其他平臺只需要繼承基類就可以了)。

這個爬蟲基類大致的結(jié)構(gòu)如下:

Puppeteer怎么用來做爬蟲

我們不用理解所有的方法,只需要知道我們啟動的入口是 run 這個方法就好了。

所有方法都加上了 async,表示這個方法將返回 Promise,如果需要以同步的形式調(diào)用,必須加上 await 這個關(guān)鍵字。

run 方法的內(nèi)容如下:

  async run() {
    // 初始化
    await this.init()

    if (this.task.authType === constants.authType.LOGIN) {
      // 登陸
      await this.login()
    } else {
      // 使用Cookie
      await this.setCookies()
    }

    // 導(dǎo)航至編輯器
    await this.goToEditor()

    // 輸入編輯器內(nèi)容
    await this.inputEditor()

    // 發(fā)布文章
    await this.publish()

    // 關(guān)閉瀏覽器
    await this.browser.close()
  }

可以看到,爬蟲將首先初始化,完成一些基礎(chǔ)配置;然后根據(jù)任務(wù)的驗證類別(authType )來決定是否采用登錄或 Cookie 的方式來通過網(wǎng)站驗證(本文只考慮登錄驗證的情況);接下來就是導(dǎo)航至編輯器,然后輸入編輯器內(nèi)容;接著,發(fā)布文章;最后關(guān)閉瀏覽器,發(fā)布任務(wù)完成。

登錄
  async login() {
    logger.info(`logging in... navigating to ${this.urls.login}`)
    await this.page.goto(this.urls.login)
    let errNum = 0
    while (errNum < 10) {
      try {
        await this.page.waitFor(1000)
        const elUsername = await this.page.$(this.loginSel.username)
        const elPassword = await this.page.$(this.loginSel.password)
        const elSubmit = await this.page.$(this.loginSel.submit)
        await elUsername.type(this.platform.username)
        await elPassword.type(this.platform.password)
        await elSubmit.click()
        await this.page.waitFor(3000)
        break
      } catch (e) {
        errNum++
      }
    }

    // 查看是否登陸成功
    this.status.loggedIn = errNum !== 10

    if (this.status.loggedIn) {
      logger.info('Logged in')
    }
  }

掘金的登錄地址是 https://juejin.im/login,我們先將瀏覽器導(dǎo)航至這個地址。

這里我們循環(huán) 10 次,嘗試輸入用戶名和密碼,如果 10 次都失敗了,就設(shè)置登錄狀態(tài)為 false;反之,則設(shè)置為 true。

接著,我們用到了 page.$(selector)el.type(text) 這兩個 API ,分別用于獲取元素和輸入內(nèi)容。而最后的 elSubmit.click() 是提交表單的操作。

編輯文章

這里我們略過了跳轉(zhuǎn)到文章編輯器的步驟,因為這個很簡單,只需要調(diào)用 page.goto(url) 就可以了,后面會貼出源碼地址供大家參考。

輸入編輯器的代碼如下:

  async inputEditor() {
    logger.info(`input editor title and content`)
    // 輸入標(biāo)題
    await this.page.evaluate(this.inputTitle, this.article, this.editorSel, this.task)
    await this.page.waitFor(3000)

    // 輸入內(nèi)容
    await this.page.evaluate(this.inputContent, this.article, this.editorSel)
    await this.page.waitFor(3000)

    // 輸入腳注
    await this.page.evaluate(this.inputFooter, this.article, this.editorSel)
    await this.page.waitFor(3000)

    await this.page.waitFor(10000)

    // 后續(xù)處理
    await this.afterInputEditor()
  }

首先輸入標(biāo)題,調(diào)用了 page.evaluate 這個前端執(zhí)行函數(shù),傳入 this.inputTitle 輸入標(biāo)題這個回調(diào)函數(shù),以及其他參數(shù);接著同樣的原理,調(diào)用輸入內(nèi)容回調(diào)函數(shù);然后是輸入腳注;最后,調(diào)用后續(xù)處理函數(shù)。


下面我們詳細(xì)看看 this.inputTitle 這個函數(shù):

  async inputTitle(article, editorSel, task) {
    const el = document.querySelector(editorSel.title)
    el.focus()
    el.select()
    document.execCommand('delete', false)
    document.execCommand('insertText', false, task.title || article.title)
  }

我們首先通過前端的公開接口 document.querySelector(selector) 獲取標(biāo)題的元素,為了防止標(biāo)題有 placeholder,我們用 el.focus()(獲取焦點(diǎn))、el.select()(全選)、document.execCommand('delete', false)(刪除)來刪除已有的 placeholder。然后我們通過 document.execCommand('insertText', false, text) 來輸入標(biāo)題內(nèi)容。

接下來,是輸入內(nèi)容,代碼如下(它的原理與輸入標(biāo)題類似):

  async inputContent(article, editorSel) {
    const el = document.querySelector(editorSel.content)
    el.focus()
    el.select()
    document.execCommand('delete', false)
    document.execCommand('insertText', false, article.content)
  }

有人可能會問,為什么不用 el.type(text) 來輸入內(nèi)容,反而要大費(fèi)周章的用 document.execCommand 來實現(xiàn)輸入呢?

這里我們不用前者的原因,是因為它是完全模擬人的敲打鍵盤操作的,這樣會破壞已有的內(nèi)容格式。而如果用后者的話,可以一次性的將內(nèi)容輸入進(jìn)來。

我們在基類 BaseSpider 中預(yù)留了一個方法來完成選擇分類、標(biāo)簽等操作,在繼承后的類 JuejinSpider 中是這樣的:

    async afterInputEditor() {
        // 點(diǎn)擊發(fā)布文章
        const elPubBtn = await this.page.$('.publish-popup')
        await elPubBtn.click()
        await this.page.waitFor(5000)

        // 選擇類別
        await this.page.evaluate((task) => {
            document.querySelectorAll('.category-list > .item').forEach(el => {
                if (el.textContent === task.category) {
                    el.click()
                }
            })
        }, this.task)
        await this.page.waitFor(5000)

        // 選擇標(biāo)簽
        const elTagInput = await this.page.$('.tag-input > input')
        await elTagInput.type(this.task.tag)
        await this.page.waitFor(5000)
        await this.page.evaluate(() => {
            document.querySelector('.suggested-tag-list > .tag:nth-child(1)').click()
        })
        await this.page.waitFor(5000)
    }
發(fā)布

發(fā)布操作相對來說比較簡單了,只需要點(diǎn)擊發(fā)布的那個按鈕就可以了。代碼如下:

  async publish() {
    logger.info(`publishing article`)
    // 發(fā)布文章
    const elPub = await this.page.$(this.editorSel.publish)
    await elPub.click()
    await this.page.waitFor(10000)

    // 后續(xù)處理
    await this.afterPublish()
  }

this.afterPublish 是用來處理驗證發(fā)文狀態(tài)和獲取發(fā)布 URL 的,這里限于篇幅不詳細(xì)介紹了。

介紹了如何使用 Puppeteer 來操作 Chromium 瀏覽器在掘金上發(fā)布文章。

很多人用 Puppeteer 來抓取數(shù)據(jù),但我們認(rèn)為這種效率較低,而且開銷較大,不適合大規(guī)模抓取。

相反, Puppeteer 更適合做一些自動化的工作,例如操作瀏覽器發(fā)布文章、發(fā)布帖子、提交表單等等。

Puppeteer 自動化工具很類似 RPA(Robotic Process Automation),都是自動化一些繁瑣的、重復(fù)性的工作,只不過后者不僅限于瀏覽器,其范圍(Scope)是基于整個操作系統(tǒng)的,功能更強(qiáng)大,但是開銷也更大。

Puppeteer 作為相對輕量級的自動化工具,很適合用來做一些網(wǎng)頁自動化操作作業(yè)。本文介紹的 Puppeteer 實戰(zhàn)內(nèi)容也是開源一文多發(fā)平臺項目 ArtiPub 的一部分,有興趣的同學(xué)可以去嘗試一下。


夜幕團(tuán)隊成立于 2019 年,團(tuán)隊包括崔慶才、周子淇、陳祥安、唐軼飛、馮威、蔡晉、戴煌金、張冶青和韋世東。

涉獵的編程語言包括但不限于 Python、Rust、C++、Go,領(lǐng)域涵蓋爬蟲、深度學(xué)習(xí)、服務(wù)研發(fā)、對象存儲等。團(tuán)隊非正亦非邪,只做認(rèn)為對的事情,請大家小心。

上述就是小編為大家分享的Puppeteer怎么用來做爬蟲了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。

本文名稱:Puppeteer怎么用來做爬蟲
分享鏈接:http://bm7419.com/article8/gochop.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供動態(tài)網(wǎng)站、定制開發(fā)、網(wǎng)頁設(shè)計公司建站公司、全網(wǎng)營銷推廣、營銷型網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

h5響應(yīng)式網(wǎng)站建設(shè)