基於puppeteer模擬登陸抓取頁面

關於熱圖

在網站分析行業中,網站熱圖可以很好的反應用戶在網站的操做行爲,具體分析用戶的喜愛,對網站進行鍼對性的優化,一個熱圖的例子(來源於ptengine)javascript

ptengine點擊熱圖

上圖中能很清晰的看到用戶關注點在那,咱們不關注產品中熱圖的功能如何,本篇文章就熱圖的實現作一下簡單的分析和總結。css

熱圖主流的實現方式

通常實現熱圖顯示須要通過以下階段:html

  1. 獲取網站頁面
  2. 獲取通過處理後的用戶數據
  3. 繪製熱圖
    本篇主要聚焦於階段1來詳細的介紹一下主流的在熱圖中獲取網站頁面的實現方式
  4. 使用iframe直接嵌入用戶網站
  5. 抓取用戶頁面保存到本地,經過iframe嵌入本地資源(所謂本地資源這裏認爲是分析工具這一端)

兩種方式各有各的優缺點,首先第一種直接嵌入用戶網站,這個有必定的限制條件,好比若是用戶網站爲了防止iframe劫持,不容許iframe嵌套(設置meta X-FRAME-OPTIONS 爲sameorgin 或者直接設置http header ,甚至直接經過js來控制java

if(window.top !== window.self){ window.top.location = window.location;}

),這種狀況下就須要客戶網站作一部分工做才能夠被分析工具的iframe加載,使用起來不必定那麼方便,由於並非全部的須要檢測分析的網站用戶均可以管理網站的。瀏覽器

第二種方式,直接抓取網站頁面到本地服務器,而後瀏覽的是本機服務器上抓取的頁面,這種狀況下頁面已通過來了,咱們就能夠隨心所欲了,首先咱們繞過了X-FRAME-OPTIONS 爲sameorgin的問題,只須要解決js控制的問題,對於抓取的頁面來講,咱們能夠經過特殊的對應來處理(好比移除對應的js控制,或者添加咱們本身的js);可是這種方式也有不少的不足:一、沒法抓取spa頁面,沒法抓取須要用戶登陸受權的頁面,沒法抓取用戶設置了白明白的頁面等等。服務器

兩種方式都存在https 和 http資源因爲同源策略引發的另外一個問題,https站沒法加載http資源,因此若是爲了最好的兼容性,熱圖分析工具須要被應用http協議,固然具體能夠根據訪問的客戶網站而具體分站優化。async

抓取網站頁面如何優化

這裏咱們針對抓取網站頁面遇到的問題基於puppeteer作一些優化,提升抓取成功的機率,主要優化如下兩種頁面:工具

  1. spa頁面
    spa頁面在當前頁算是主流了,可是它總所周知的是其對搜索引擎的不友好;一般的頁面抓取程序其實就是一個簡單的爬蟲,其過程一般都是發起一個http get 請求到用戶網站(應該是用戶網站服務器)。這種抓取方式自己就會有問題問題,首先,直接請求的是用戶服務器,用戶服務器對非瀏覽器的agent 應該會有不少限制,須要繞過處理;其次,請求返回的是原始內容,須要在瀏覽器中經過js渲染的部分沒法獲取(固然,在iframe嵌入後,js執行仍是會再必定程度上彌補這個問題),最後若是頁面是spa頁面,那麼此時獲取的只是模板,在熱圖中顯示效果很是不友好。
    針對這種狀況,若是基於puppeteer來作,流程就變成了
    puppeteer啓動瀏覽器打開用戶網站-->頁面渲染-->返回渲染後結果,簡單的用僞代碼實現以下:
const puppeteer = require('puppeteer');

async getHtml = (url) =>{
    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    await page.goto(url);
    return await page.content();
}

這樣咱們拿到的內容就是渲染後的內容,不管頁面的渲染方式如何(客戶端渲染抑或服務端)性能

須要登陸的頁面

對於須要登陸頁面其實分爲多種狀況:優化

  • 須要登陸才能夠查看頁面,若是沒有登陸,則跳轉到login頁面(各類管理系統)
    對於這種類型的頁面咱們須要作的就是模擬登陸,所謂模擬登陸就是讓瀏覽器去登陸,這裏須要用戶提供對應網站的用戶名和密碼,而後咱們走以下的流程:
    訪問用戶網站-->用戶網站檢測到未登陸跳轉到login-->puppeteer控制瀏覽器自動登陸後跳轉到真正須要抓取的頁面,可用以下僞代碼來講明:
const puppeteer = require("puppeteer");
async autoLogin =(url)=>{
     const browser = await puppeteer.launch();
     const page =await browser.newPage();
     await page.goto(url);
     await page.waitForNavigation();

     //登陸
     await page.type('#username',"用戶提供的用戶名");
     await page.type('#password','用戶提供的密碼');

     await page.click('#btn_login');

    //頁面登陸成功後,須要保證redirect 跳轉到請求的頁面
     await page.waitForNavigation();

     return await page.content();
}
  • 登陸與否均可以查看頁面,只是登陸後看到內容會全部不一樣 (各類電商或者portal頁面)

這種狀況處理會比較簡單一些,能夠簡單的認爲是以下步驟:

經過puppeteer啓動瀏覽器打開請求頁面-->點擊登陸按鈕-->輸入用戶名和密碼登陸 -->從新加載頁面

基本代碼以下圖:

const puppeteer = require("puppeteer");
async autoLoginV2 =(url)=>{
     const browser = await puppeteer.launch();
     const page =await browser.newPage();
     await page.goto(url);

     await page.click('#btn_show_login');

     //登陸
     await page.type('#username',"用戶提供的用戶名");
     await page.type('#password','用戶提供的密碼');

     await page.click('#btn_login');

    //頁面登陸成功後,是否須要reload 根據實際狀況來肯定
     await page.reload();

     return await page.content();
}

總結

明天總結吧,今天下班了。
補充(還昨天的債):基於puppeteer雖然能夠很友好的抓取頁面內容,可是也存在這不少的侷限

  1. 抓取的內容爲渲染後的原始html,即資源路徑(css、image、javascript)等都是相對路徑,保存到本地後沒法正常顯示,須要特殊處理(js不須要特殊處理,甚至能夠移除,由於渲染的結構已經完成)
  2. 經過puppeteer抓取頁面性能會比直接http get 性能會差一些,由於多了渲染的過程
  3. 一樣沒法保證頁面的完整性,只是很大的提升了完整的機率,雖然經過page對象提供的各類wait 方法可以解決這個問題,可是網站不一樣,處理方式就會不一樣,沒法複用。
相關文章
相關標籤/搜索