chromedp入門

  • 2020 年 6 月 23 日
  • 筆記

chromedp入門

chromedp是什麼?

chromedp是go寫的,支持Chrome DevTools Protocol 的一個驅動瀏覽器的庫。並且它不需要依賴其他的外界服務(比如 Selenium 和 PhantomJs)。

Chrome DevTools Protocol (CDP)

Chrome DevTools Protocol (CDP) 的主頁在://chromedevtools.github.io/devtools-protocol/。 它提供一系列的接口來查看,檢查,調整並且檢查 Chromium 的性能。Chrome 的開發者工具就是使用這一系列的接口,並且 Chrome 開發者工具來維護這些接口。

所謂 CDP 的協議,本質上是什麼呢?本質上是基於 websocket 的一種協議。比如
20200622105228
在我們打開 webtool 調試工具的時候,其實調試工具也是一個web頁面,兩個web頁面通過websocket建立了一個聯繫。
所以我們如果寫了一個客戶端程序,也和目標頁面創建一個基於 CDP 的 websocket連接,我們也可以通過這個協議來對頁面進行操作。

如何打開 Protocol Monitor

在chrome的開發者工具中
20200622105732
打開實驗選項 Protocol Monitor
20200622110118
重啟chrome,在console的更多裏面就可以打開對應的 Monitor
20200622110240

CDP 協議內容

我們從 Protocol Monitor 面板中可以看到,其中有幾個字樣,Method,Request,Response。
這裡的 Method 就是對應官網 //chromedevtools.github.io/devtools-protocol/ 左側每個Domain的 Event。

20200622151635

這裡的每個Method方法可能是調試頁面給目標頁面發送的,但是更多是目標頁面給調試頁面發送的消息。所以我們需要讀懂每個Method的內容。不過很可惜,我個人感覺官網的每個Method文檔的描述寫的實在是太簡單了,也沒有看到更詳細的描述,只能通過名字和事件來猜測每個Method意思了。

chromedp 使用

chromedp的使用最快的方法就是看 //github.com/chromedp/examples 這個項目

基本我們可以熟悉最常用的幾個方法了:

  • chromedp.NewContext() 初始化chromedp的上下文,後續這個頁面都使用這個上下文進行操作
  • chromedp.Run() 運行一個chrome的一系列操作
  • chromedp.Navigate() 將瀏覽器導航到某個頁面
  • chromedp.WaitVisible() 等候某個元素可見,再繼續執行。
  • chromedp.Click() 模擬鼠標點擊某個元素
  • chromedp.Value() 獲取某個元素的value值
  • chromedp.ActionFunc() 再當前頁面執行某些自定義函數
  • chromedp.Text() 讀取某個元素的text值
  • chromedp.Evaluate() 執行某個js,相當於控制台輸入js
  • network.SetExtraHTTPHeaders() 截取請求,額外增加header頭
  • chromedp.SendKeys() 模擬鍵盤操作,輸入字符
  • chromedp.Nodes() 根據xpath獲取某些元素,並存儲進入數組
  • chromedp.NewRemoteAllocator
  • chromedp.OuterHTML() 獲取元素的outer html
  • chromedp.Screenshot() 根據某個元素截圖
  • page.CaptureScreenshot() 截取整個頁面的元素
  • chromedp.Submit() 提交某個表單
  • chromedp.WaitNotPresent() 等候某個元素不存在,比如「正在搜索。。。」
  • chromedp.Tasks{} 一系列Action組成的任務

實踐

我們嘗試打開 //www.cnblogs.com/ 的首頁,然後獲取所有文章的標題和鏈接:

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/chromedp/cdproto/cdp"
	"github.com/chromedp/chromedp"
)

func main() {

	ctx, cancel := chromedp.NewContext(
		context.Background(),
		chromedp.WithLogf(log.Printf),
	)
	defer cancel()

	var nodes []*cdp.Node
	err := chromedp.Run(ctx,
		chromedp.Navigate("//www.cnblogs.com/"),
		chromedp.WaitVisible(`#footer`, chromedp.ByID),
		chromedp.Nodes(`.//a[@class="titlelnk"]`, &nodes),
	)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("get nodes:", len(nodes))
	// print titles
	for _, node := range nodes {
		fmt.Println(node.Children[0].NodeValue, ":", node.AttributeValue("href"))
	}
}