Node使用Puppeteer做爬蟲
這次帶給大家Node使用Puppeteer做爬蟲,Node使用Puppeteer做爬蟲的注意事項有哪些,下面就是實戰案例,一起來看一下。
架構圖
Puppeteer架構圖
Puppeteer 透過devTools 與browser 通訊
#Browser 一個可以擁有多個頁面的瀏覽器(chroium)實例
#Page 至少含有一個Frame 的頁面
Frame 至少還有一個用於執行javascript 的執行環境,也可以拓展多個執行環境
#前言
##最近想要入手一台桌上型電腦,筆記本的i5在開啟網頁和vsc的時候有明顯卡頓的情況,因此打算配1台i7 GTX1070TI or GTX1080TI的電腦,直接在淘寶上搜需要翻頁太多,並且圖片太多,腦容量接受不了,因此想爬一些數據,利用圖形化分析一下最近價格的走勢。因此寫了一個用Puppeteer寫了一個爬蟲爬去相關資料。什麼是Puppeteer?
Puppeteer is a Node library which provides a high-level API to control headless Chrome or Chromium over the DevTools Protocol. It can also be configured to use full (non-headless) Chrome or Chromium.簡而言之,這貨是一個提供高級API的node庫,能夠透過devtool控制headless模式的chrome或者chromium,它可以在headless模式下模擬任何的人為操作。和cheerio的區別
cherrico本質上只是一個使用類似jquery的語法操作HTML文檔的庫,使用cherrico爬取數據,只是請求到靜態的HTML文檔,如果網頁內部的資料是透過ajax動態取得的,那麼便爬去不到的相應的資料。而Puppeteer能夠模擬一個瀏覽器的運作環境,能夠請求網站訊息,並運行網站內部的邏輯。然後再透過WS協議動態的獲取頁面內部的數據,並能夠進行任何模擬的操作(點擊、滑動、hover等),並且支援跳轉頁面,多頁面管理。甚至能注入node上的腳本到瀏覽器內部環境運行,總之,你能對一個網頁做的操作它都能做,你不能做的它也能做。開始
本文不是手把手教程,因此需要你有基本的Puppeteer API常識,如果不懂,請先看看官方介紹Puppeteer官方網站
PuppeteerAPI
// 引入一些需要用到的库以及一些声明 import * as puppeteer from 'puppeteer' // 引入Puppeteer import mongo from '../lib/mongoDb' // 需要用到的 mongodb库,用来存取爬取的数据 import chalk from 'chalk' // 一个美化 console 输出的库 const log = console.log // 缩写 console.log const TOTAL_PAGE = 50 // 定义需要爬取的网页数量,对应页面下部的跳转链接 // 定义要爬去的数据结构 interface IWriteData { link: string // 爬取到的商品详情链接 picture: string // 爬取到的图片链接 price: number // 价格,number类型,需要从爬取下来的数据进行转型 title: string // 爬取到的商品标题 } // 格式化的进度输出 用来显示当前爬取的进度 function formatProgress (current: number): string { let percent = (current / TOTAL_PAGE) * 100 let done = ~~(current / TOTAL_PAGE * 40) let left = 40 - done let str = `当前进度:[${''.padStart(done, '=')}${''.padStart(left, '-')}] ${percent}%` return str }
// 因为我们需要用到大量的 await 语句,因此在外层包裹一个 async function async function main() { // Do something } main()
// 进入代码的主逻辑 async function main() { // 首先通过Puppeteer启动一个浏览器环境 const browser = await puppeteer.launch() log(chalk.green('服务正常启动')) // 使用 try catch 捕获异步中的错误进行统一的错误处理 try { // 打开一个新的页面 const page = await browser.newPage() // 监听页面内部的console消息 page.on('console', msg => { if (typeof msg === 'object') { console.dir(msg) } else { log(chalk.blue(msg)) } }) // 打开我们刚刚看见的淘宝页面 await page.goto('https://s.taobao.com/search?q=gtx1080&imgfile=&js=1&stats_click=search_radio_all%3A1&initiative_id=staobaoz_20180416&ie=utf8') log(chalk.yellow('页面初次加载完毕')) // 使用一个 for await 循环,不能一个时间打开多个网络请求,这样容易因为内存过大而挂掉 for (let i = 1; i <= TOTAL_PAGE; i++) { // 找到分页的输入框以及跳转按钮 const pageInput = await page.$(`.J_Input[type='number']`) const submit = await page.$('.J_Submit') // 模拟输入要跳转的页数 await pageInput.type('' + i) // 模拟点击跳转 await submit.click() // 等待页面加载完毕,这里设置的是固定的时间间隔,之前使用过page.waitForNavigation(),但是因为等待的时间过久导致报错(Puppeteer默认的请求超时是30s,可以修改),因为这个页面总有一些不需要的资源要加载,而我的网络最近日了狗,会导致超时,因此我设定等待2.5s就够了 await page.waitFor(2500) // 清除当前的控制台信息 console.clear() // 打印当前的爬取进度 log(chalk.yellow(formatProgress(i))) log(chalk.yellow('页面数据加载完毕')) // 处理数据,这个函数的实现在下面 await handleData() // 一个页面爬取完毕以后稍微歇歇,不然太快淘宝会把你当成机器人弹出验证码(虽然我们本来就是机器人) await page.waitFor(2500) } // 所有的数据爬取完毕后关闭浏览器 await browser.close() log(chalk.green('服务正常结束')) // 这是一个在内部声明的函数,之所以在内部声明而不是外部,是因为在内部可以获取相关的上下文信息,如果在外部声明我还要传入 page 这个对象 async function handleData() { // 现在我们进入浏览器内部搞些事情,通过page.evaluate方法,该方法的参数是一个函数,这个函数将会在页面内部运行,这个函数的返回的数据将会以Promise的形式返回到外部 const list = await page.evaluate(() => { // 先声明一个用于存储爬取数据的数组 const writeDataList: IWriteData[] = [] // 获取到所有的商品元素 let itemList = document.querySelectorAll('.item.J_MouserOnverReq') // 遍历每一个元素,整理需要爬取的数据 for (let item of itemList) { // 首先声明一个爬取的数据结构 let writeData: IWriteData = { picture: undefined, link: undefined, title: undefined, price: undefined } // 找到商品图片的地址 let img = item.querySelector('img') writeData.picture = img.src // 找到商品的链接 let link: HTMLAnchorElement = item.querySelector('.pic-link.J_ClickStat.J_ItemPicA') writeData.link = link.href // 找到商品的价格,默认是string类型 通过~~转换为整数number类型 let price = item.querySelector('strong') writeData.price = ~~price.innerText // 找到商品的标题,淘宝的商品标题有高亮效果,里面有很多的span标签,不过一样可以通过innerText获取文本信息 let title: HTMLAnchorElement = item.querySelector('.title>a') writeData.title = title.innerText // 将这个标签页的数据push进刚才声明的结果数组 writeDataList.push(writeData) } // 当前页面所有的返回给外部环境 return writeDataList }) // 得到数据以后写入到mongodb const result = await mongo.insertMany('GTX1080', list) log(chalk.yellow('写入数据库完毕')) } } catch (error) { // 出现任何错误,打印错误消息并且关闭浏览器 console.log(error) log(chalk.red('服务意外终止')) await browser.close() } finally { // 最后要退出进程 process.exit(0) } }
思考
#1、為什麼要用Typescript?
因為Typescript就是好用啊,我也背不住Puppeteer的全部API,也不想每一個都查,所以使用TS就能智能提醒了,也能避免因為拼寫導致的低級錯誤。基本上用了TS以後,敲程式碼都能一遍過2、爬蟲的效能問題?
因為Puppeteer會啟動一個瀏覽器,執行內部的邏輯,所以佔用的記憶體是蠻多的,看了看控制台,這個node進程大概佔用300MB左右的記憶體。 我的頁面是一個個爬的,如果想更快的爬取可以啟動多個進程,注意,V8是單線程的,所以在一個進程內部打開多個頁面是沒有意義的,需要配置不同的參數來開啟不同的node進程,當然也可以透過node的cluster(集群)實現,本質都是一樣的我在爬取的過程中也設定了不同的等待時間,一方面是為了等待網頁的加載,一方面避免淘寶識別到我是爬蟲彈驗證碼
3、Puppeteer的其它功能
這裡僅僅利用了Puppeteer的一些基本特性,實際上Puppeteer還有更多的功能。例如引入node上的處理函數在瀏覽器內部執行,將目前頁面儲存為pdf或png圖片。並且還可以透過const browser = await puppeteer.launch({ headless: false })啟動一個帶有介面效果的瀏覽器,你可以看見你的爬蟲是如何運作的。另外一些需要登入的網站,如果你不想識別驗證碼委託第三方進行處理,你也可以關閉headless,然後在程式中設定等待時間,手動完成一些驗證從而達到登入的目的。
當然google製作了一個這麼牛逼的函式庫可不只是用來做爬蟲爬取資料的,這個函式庫也用來當作一些自動化的效能分析、介面測試、前端網站監控等
4、一些其它方面的思考
總得來說製作爬蟲爬取資料是一項較為複雜並考察多項基本功的練習項目,在這個爬蟲裡多次使用到了async,這就需要對async、Promise等相關知識充分的了解。在分析DOM收集資料時,也多次利用了原生的方法取得DOM屬性(如果網站有jquery也可以直接用,沒有的話需要外部注入,在typescript下需要進行一些配置,避免報錯未識別的$變量,這樣就可以透過jquery語法操作DOM),檢視了對DOM相關API的熟練程度。
另外這只是一個面向過程的編程,我們完全可以將它封裝為一個類別進行操作,這也考察了對ES的OOP理解
相信看了本文案例你已經掌握了方法,更多精彩請關注php中文網其它相關文章!
推薦閱讀:
以上是Node使用Puppeteer做爬蟲的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

nvm刪除node的方法:1、下載「nvm-setup.zip」並將其安裝在C碟;2、設定環境變量,並透過「nvm -v」指令查看版本號;3、使用「nvm install」指令安裝node;4、透過「nvm uninstall」指令刪除已安裝的node即可。

怎麼處理文件上傳?以下這篇文章為大家介紹一下node專案中如何使用express來處理文件的上傳,希望對大家有幫助!

這段時間在開發一個騰訊文檔全品類通用的HTML 動態服務,為了方便各品類接入的生成與部署,也順應上雲的趨勢,考慮使用Docker 的方式來固定服務內容,統一進行製品版本的管理。這篇文章就將我在服務 Docker 化的過程中累積起來的優化經驗分享出來,供大家參考。

這篇文章跟大家分享Node的進程管理工具“pm2”,聊聊為什麼需要pm2、安裝和使用pm2的方法,希望對大家有幫助!

PiNetwork節點詳解及安裝指南本文將詳細介紹PiNetwork生態系統中的關鍵角色——Pi節點,並提供安裝和配置的完整步驟。 Pi節點在PiNetwork區塊鏈測試網推出後,成為眾多先鋒積極參與測試的重要環節,為即將到來的主網發布做準備。如果您還不了解PiNetwork,請參考Pi幣是什麼?上市價格多少? Pi用途、挖礦及安全性分析。什麼是PiNetwork? PiNetwork項目始於2019年,擁有其專屬加密貨幣Pi幣。該項目旨在創建一個人人可參與

npm node gyp失敗是因為“node-gyp.js”跟“Node.js”版本不匹配,其解決辦法:1、透過“npm cache clean -f”清除node快取;2、透過“npm install -g n”安裝n模組;3、透過「n v12.21.0」指令安裝「node v12.21.0」版本即可。

如何用pkg打包nodejs可執行檔?以下這篇文章跟大家介紹一下使用pkg將Node專案打包為執行檔的方法,希望對大家有幫助!

身份驗證是任何網路應用程式中最重要的部分之一。本教程討論基於令牌的身份驗證系統以及它們與傳統登入系統的差異。在本教程結束時,您將看到一個用Angular和Node.js編寫的完整工作演示。傳統身份驗證系統在繼續基於令牌的身份驗證系統之前,讓我們先來看看傳統的身份驗證系統。使用者在登入表單中提供使用者名稱和密碼,然後點擊登入。發出請求後,透過查詢資料庫在後端驗證使用者。如果請求有效,則使用從資料庫中獲取的使用者資訊建立會話,然後在回應頭中傳回會話訊息,以便將會話ID儲存在瀏覽器中。提供用於存取應用程式中受
