如今動態渲染的頁面越來越多,爬蟲們或多或少都需要用到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進行動態網頁的渲染和操作就介紹完了。
希望這篇文章能給你帶來幫助,如有錯誤之處,歡迎交流指正。