1. 程式人生 > >基於puppeteer模擬登錄抓取頁面

基於puppeteer模擬登錄抓取頁面

分享圖片 load() Go 重新 直接 req 用戶 red cat

關於熱圖

在網站分析行業中,網站熱圖能夠很好的反應用戶在網站的操作行為,具體分析用戶的喜好,對網站進行針對性的優化,一個熱圖的例子(來源於ptengine)

技術分享圖片

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

熱圖主流的實現方式

一般實現熱圖顯示需要經過如下階段:

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

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

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

),這種情況下就需要客戶網站做一部分工作才可以被分析工具的iframe加載,使用起來不一定那麽方便,因為並不是所有的需要檢測分析的網站用戶都可以管理網站的。

第二種方式,直接抓取網站頁面到本地服務器,然後瀏覽的是本機服務器上抓取的頁面,這種情況下頁面已經過來了,我們就可以為所欲為了,首先我們繞過了X-FRAME-OPTIONS 為sameorgin的問題,只需要解決js控制的問題,對於抓取的頁面來說,我們可以通過特殊的對應來處理(比如移除對應的js控制,或者添加我們自己的js);但是這種方式也有很多的不足:1、無法抓取spa頁面,無法抓取需要用戶登錄授權的頁面,無法抓取用戶設置了白明白的頁面等等。

兩種方式都存在https 和 http資源由於同源策略引起的另一個問題,https站無法加載http資源,所以如果為了最好的兼容性,熱圖分析工具需要被應用http協議,當然具體可以根據訪問的客戶網站而具體分站優化。

抓取網站頁面如何優化

這裏我們針對抓取網站頁面遇到的問題基於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模擬登錄抓取頁面