如今動态渲染的頁面越來越多,爬蟲們或多或少都需要用到headless browser來渲染待爬取的頁面。
而最近廣泛使用的headless browser解決方案PhantomJS已經宣布不再繼續維護,轉而推薦使用headless chrome。
那麼headless chrome究竟是什麼呢,Headless Chrome 是 Chrome 浏覽器的無界面形态,可以在不打開浏覽器的前提下,使用所有 Chrome 支援的特性運作你的程式。
簡而言之,除了沒有圖形界面,headless chrome具有所有現代浏覽器的特性,可以像在其他現代浏覽器裡一樣渲染目标網頁,并能進行網頁截圖,擷取cookie,擷取html等操作。
詳細資訊可以在這擷取:https://developers.google.cn/web/updates/2017/04/headless-chrome
有關headless chrome如何使用網上有許多不錯的文章,這裡就不重複了。
想要在golang程式裡使用headless chrome,需要借助一些開源庫,實作和headless chrome互動的庫有很多,這裡選擇chromedp,接口和Selenium類似,易上手。
安裝:
go get -u github.com/chromedp/chromedp
引入:
import (
"github.com/chromedp/chromedp"
// runner用于配置headless chrome
"github.com/chromedp/chromedp/runner" // 新版本中不需要再導入這個包了
)
建立headless chrome執行個體,每一個執行個體就相當于一個浏覽器,可以用它浏覽、調試網頁内容,預設情況下chromedp會直接啟動帶GUI的chrome,是以需要使用runner啟動headless chrome。預設端口為9222,可以自定義。
需要注意,chromedp在0.1.4版本中對api進行了較大的改動,是以接下來的示例中我會給出新api的用法,同時保留0.1.3及以前版本适用的例子。
// NewHeadless 建立headless chrome執行個體
// chromedp内部有自己的逾時設定,你也可以通過ctx來設定更短的逾時
func NewHeadless(ctx context.Context, starturl string) (*chromedp.CDP, error) {
// runner.Flag設定啟動headless chrome時的指令行參數
// runner.URL設定啟動時打開的URL
// Windows使用者需要設定runner.Flag("disable-gpu", true),具體資訊參見文檔的FAQ
run, err := runner.New(runner.Flag("headless", true),
runner.URL(starturl))
if err != nil {
return nil, err
}
// run.Start啟動執行個體
err = run.Start(ctx)
if err != nil {
return nil, err
}
// 預設情況chromedp會輸出大量log,因為是示例是以選擇屏蔽,dropChromeLogs為自定義函數,形式為func(string, ...interface{}){}
// 使用runner初始化chromedp執行個體
// 執行個體在使用完畢後需要調用c.Shutdown()來釋放資源
c, err := chromedp.New(ctx, chromedp.WithRunner(run), chromedp.WithErrorf(dropChromeLogs))
if err != nil {
return nil, err
}
return c, nil
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
cdp := NewHeadless(ctx, "www.cnblogs.com")
下面是0.1.4版本的api:
// 新版本中取消了cdp,将broeser對象和context合并在一起,友善了我們的操作
func NewHeadless() (context.Context, context.CancelFunc) {
opts := make([]chromedp.ExecAllocatorOption, 0)
opts = append(opts, chromedp.ProxyServer("http://127.0.0.1:8118"))
opts = append(opts, chromedp.Flag("headless", true))
allocator, cancel := chromedp.NewAllocator(context.Background(), chromedp.WithExecAllocator(opts...))
return allocator, cancel
}
ctxt, cancel1 := NewHeadless()
defer cance1l()
c, cancel2 := chromedp.NewContext(ctxt)
defer cancel2()
新版本中不會輸出多餘的log,同時也會預設啟用headless模式。
如果你需要在新版本的chromedp啟動執行個體時指定一個url,你可以這樣做:
broswer := chromedp.NewBroswer(c, startURL)
chromedp.FromContext(c).Browser = browser
執行個體啟動後我們就能通過這個執行個體來通路你想爬取的URL了。
chromedp的執行個體類型為*chromedp.CDP,它擁有一個func (c *CDP) Run(ctxt context.Context, a Action) error 方法來執行所有的操作。
在新版本中chromedp通過Run方法執行所有操作,chromedp.CDP對象被chrome.Context取代,其原型為func Run(ctx context.Context, actions ...Action) error
Action是chromedp的api傳回的對象,代表對headless chrome的一個操作,多個操作可以放入chromedp.Tasks裡,它是一個元素為Action的slice,也可以作為Run的參數調用。
下面是部分常用的api:
// chromedp.Sleep使headless chrome睡眠d表示的時間長度
func Sleep(d time.Duration) Action
// chromedp.Navigate使浏覽器通路參數給出的URL
func Navigate(urlstr string) Action
// chromedp.SendKeys向指定的html元素内輸入内容
// sel是選擇器字元串或是選擇器要求的資料類型
// opts指定使用何種選擇器
// 常用的選擇器有:
// chromedp.ByID:根據id來選擇元素
// chromedp.ByQuery:根據DOM.querySelector的規則選擇元素
func SendKeys(sel interface{}, v string, opts ...QueryOption) Action
// chromedp.Submit将指定的元素(通常是form)送出
func Submit(sel interface{}, opts ...QueryOption) Action
// chromedp.WaitReady等待指定元素加載完畢
func WaitReady(sel interface{}, opts ...QueryOption) Action
// chromedp.Click在指定元素上觸發滑鼠點選事件
func Click(sel interface{}, opts ...QueryOption) Action
// chromedp.OuterHTML擷取指定元素的HTML代碼(包括其子元素)
// html參數用于存放傳回的HTML
func OuterHTML(sel interface{}, html *string, opts ...QueryOption) Action
一個擷取頁面内容的小例子,更多例子在 https://github.com/chromedp/examples
// 擷取服務清單
func GetServiceList(res *string) chromedp.Tasks {
return chromedp.Tasks{
// 通路服務清單
chromedp.Navigate(ServiceListURL),
// 等待直到body加載完畢
chromedp.WaitReady("servicesList", chromedp.ByID),
// 選擇顯示可用服務
chromedp.Click("statusActive", chromedp.ByID),
// 等待清單渲染
chromedp.Sleep(2 * time.Second),
// 擷取擷取服務清單HTML
chromedp.OuterHTML("#servicesList table", res, chromedp.ByQuery),
}
}
var html string
// cdp是chromedp執行個體
// ctx是建立cdp時使用的context.Context
err := cdp.Run(ctx, GetServiceList(&html) )
if err != nil {
// 錯誤處理
}
// 成功取得HTML内容進行後續處理
fmt.Println(html)
新版本的示例:
var html string
// ctxt是chromedp的執行個體,用于執行網頁操作
err := chromedp,Run(ctxt, GetServiceList(&html))
if err != nil {
// error handle
}
// 成功取得資料
fmt.Println(html)
另外新版本中關閉chrome執行個體的方式也有所不同:
// 釋放所有資源,并等待釋放結束
cancel2()
// 官方給的是chromedp,FromContext(ctxt).Wait(),但是目前沒有實作Wait方法
// 是以你可以像這樣
chromedp.FromContext(ctxt).Broswer.Shutdown()
chromedp.FromContext(ctxt).Allocator,Wait()
因為目前新版本還很不穩定,是以推薦使用0.1.3版本。
至此golang通過chromedp(https://github.com/chromedp/chromedp)使用headless chrome進行動态網頁的渲染和操作就介紹完了。
希望這篇文章能給你帶來幫助,如有錯誤之處,歡迎交流指正。