golang使用chrome headless獲取網頁內容


如今動態渲染的頁面越來越多,爬蟲們或多或少都需要用到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進行動態網頁的渲染和操作就介紹完了。

希望這篇文章能給你帶來幫助,如有錯誤之處,歡迎交流指正。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM