天天看點

嘗試自動重定向的次數太多_GoRod:基于DP協定的Web自動化和資料抓取工具

在日常開發中我們經常可能會遇到這樣一些情景:需要使用一些自動化的手段來幫助我們測試;在擷取某些網頁的資料的時候,由于目标網頁的資料是動态的,使用傳統的資料擷取手段無法有效的抓取;需要時不時的抓取某個網頁儲存為截圖作為快照,以用于諸如競品監測分析等;諸如以上的場景可能在以前我們比較難以實作,或者現有的工具或多或少存在着某些不足。今天就來介紹一款 Go 語言寫的基于 DevTools Protocol 的工具,叫 Rod 。

Rod的Github位址:https://github.com/go-rod/rod

Rod 是什麼?

Rod 是一款直接基于 DevTools Protocol 的進階 DevTools 驅動程式,它是為了網頁自動化和抓取而設計的,Rod 還給使用者暴露了一些底層接口,以便如果缺失某些功能的話,使用者可以直接調用這些接口發送控制請求給浏覽器。

Rod 能做什麼?

既然是基于 DevTools Protocol 協定的,那基本 DevTools 能實作的功能大部分都能實作。比如浏覽器頁面控制、視窗控制、事件監聽,根據選擇器擷取網頁元素,模拟滑鼠移動點選,頁面事件監聽、觸發,網頁注入腳本、樣式表,網頁内容修改,網頁内容擷取,執行腳本,Cookie 擷取設定,頁面截圖,會話複用,頁面監控等等功能。

Rod 的工作原理是什麼?

Rod 在被調用後會嘗試使用 WebSocket 去連接配接 DevTools,如果沒有找到的話,會基于不同的系統嘗試去一些預設的路徑尋找本地浏覽器,找到就打開,沒有找到的話會嘗試去下載下傳一個再啟動。

然後 Rod 使用 JSON-RPC 去和 DevTools 互動,以便控制浏覽器。當需要控制某個具體的頁面的時候,Rod 會給這個頁面注入一個js的助手腳本,Rod 使用這個腳本來完成頁面内容的擷取,控制等。

其他同類工具存在什麼問題?

chromedp 由于需要使用複雜的類 DSL 的任務來處理主邏輯,導緻發生問題時很難通過了解代碼來解決,也很難去追蹤問題。chromedp 也無法處理 iframe,不支援影子 DOM,當程式崩潰時,會留下僵屍程序等。

selenium 由于是基于 webdriver protocol 的,功能上相對更少,比如沒法處理關閉的影子 DOM,沒辦法儲存網頁為 PDF,沒有對性能等工具的支援,由于依賴于浏覽器驅動等也使得其變得難以使用和維護。雖然 selenium 宣稱是支援跨浏覽器的,但實際上在很多浏覽器上都或多或少存在這樣或那樣的問題。

puppeteer 需要去處理複雜的 promise/async/await 問題,但在進行自動化測試的時候,更多想要同步的擷取到狀态之後再進行後續操作,對于 QA 來說不太友好。

cypress 對關閉的影子 DOM 限制很大,對跨域 iframe 來說也不太安全。

案例一:定時對某個網頁截圖

就像前面說的,有時候我們需要對某個競品網頁保持長期的關注,需要定時的儲存快照來做分析,這時候我們就可以使用 rod 來友善的完成這個需求。

這裡我寫了一個小方法,每3秒(測試用,實際環境可以調整為半天、一天等頻率)請求一次 gocn 這個網站,然後等頁面加載完成後,将整個頁面截圖并按截圖時間作為檔案名儲存到本地。

import (   "github.com/go-rod/rod"   "strconv"   "time")func monitorGocn() {   // 建立一個浏覽器的執行個體   browser := rod.New().Connect()   url := "https://gocn.vip"   // 指定要擷取的網頁   page := browser.Page(url)   // 指定視窗大小,避免截圖時截不完整   page.Window(0, 0, 1024, 768)   // 指定截圖間隔時間,我這裡是測試,就設定了3秒   interval := time.Duration(3) * time.Second   for {      select {      case          nowTimestamp := time.Now().Unix()         nowTimestampStr := strconv.Itoa(int(nowTimestamp))         // 重定向頁面,後面的參數無實意,隻是為了確定頁面重新整理         page.Navigate(url + "/?__t__=" + nowTimestampStr)         // 等待頁面加載完成         wait := page.WaitRequestIdle()         wait()         file := "screenshot/" + nowTimestampStr + ".jpg"         // 整個頁面截圖并儲存         page.ScreenshotFullPage(file)      }   }}
           

截圖的結果:

嘗試自動重定向的次數太多_GoRod:基于DP協定的Web自動化和資料抓取工具

截圖效果如下:

嘗試自動重定向的次數太多_GoRod:基于DP協定的Web自動化和資料抓取工具

案例二:自動簽到某度貼吧

這個案例主要是以自動簽到某度貼吧為例展示自動化操作,我們複用了使用者的登入資訊,設定一些需要登入的貼吧的數組,每天自動打開每個貼吧的首頁,點選簽到按鈕進行簽到。

對簽到按鈕進行元素審查,很容易得到對應的元素選擇器。

嘗試自動重定向的次數太多_GoRod:基于DP協定的Web自動化和資料抓取工具

然後我們編寫代碼:

import (   "github.com/go-rod/rod"   "github.com/go-rod/rod/lib/launcher"   "time")func signInBa() {   // 以NewUserMode形式啟動,這樣可以複用會話資訊   // 當然你也可以以普通模式啟動,自己調用程式登入   url := launcher.NewUserMode().Launch()   browser := rod.New().ControlURL(url).Connect()   // 先打開某度貼吧首頁,友善複用   page := browser.Page("https://tieba.example.com/")   // 設定視窗大小   page.Window(0, 0, 1024, 768)   // 等待頁面加載完成   wait := page.WaitRequestIdle()   wait()   // 這裡是需要簽到的貼吧的名稱   kws := []string{"php", "java", "javascript"}   for _, kw := range kws {      // 組裝貼吧位址      url := "https://tieba.example.com/f?kw=" + kw      // 前往某個貼吧首頁      page.Navigate(url)      wait = page.WaitRequestIdle()      wait()      // 點選簽到按鈕      page.Element(".j_signbtn").Click()      // 休息10秒再繼續      time.Sleep(time.Duration(10) * time.Second)   }}
           

執行後就可以看到如下效果:

嘗試自動重定向的次數太多_GoRod:基于DP協定的Web自動化和資料抓取工具

以上兩個案例也隻是抛磚引玉,充分利用 Rod 能完成的功能遠比這些強大,同時支援在 Docker 中啟動浏覽器,也就是說可以在伺服器上去使用 Rod 的所有功能,寫好的程式直接扔伺服器上就可以等他定時執行,而且支援監控,也能自己處理錯誤來傳回。

總之,用好 Rod 能使你的工作效率大大提升,也能為你完成各種定制化的自動化功能和進階抓取資料需求。