golang使用chrome headless获取网页内容

https://www.cnblogs.com/apocelipes/p/9264673.html

如今动态渲染的页面越来越多,爬虫们或多或少都需要用到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类似,易上手。

安装:

  1. go get -u github.com/chromedp/chromedp

引入:

  1. import (
  2. "github.com/chromedp/chromedp"
  3. // runner用于配置headless chrome
  4. "github.com/chromedp/chromedp/runner" // 新版本中不需要再导入这个包了
  5. )

创建headless chrome实例,每一个实例就相当于一个浏览器,可以用它浏览、调试网页内容,默认情况下chromedp会直接启动带GUI的chrome,所以需要使用runner启动headless chrome。默认端口为9222,可以自定义。

需要注意,chromedp在0.1.4版本中对api进行了较大的改动,因此接下来的示例中我会给出新api的用法,同时保留0.1.3及以前版本适用的例子。

  1. // NewHeadless 创建headless chrome实例
  2. // chromedp内部有自己的超时设置,你也可以通过ctx来设置更短的超时
  3. func NewHeadless(ctx context.Context, starturl string) (*chromedp.CDP, error) {
  4. // runner.Flag设置启动headless chrome时的命令行参数
  5. // runner.URL设置启动时打开的URL
  6. // Windows用户需要设置runner.Flag("disable-gpu", true),具体信息参见文档的FAQ
  7. run, err := runner.New(runner.Flag("headless", true),
  8. runner.URL(starturl))
  9.  
  10. if err != nil {
  11. return nil, err
  12. }
  13.  
  14. // run.Start启动实例
  15. err = run.Start(ctx)
  16. if err != nil {
  17. return nil, err
  18. }
  19.  
  20. // 默认情况chromedp会输出大量log,因为是示例所以选择屏蔽,dropChromeLogs为自定义函数,形式为func(string, ...interface{}){}
  21. // 使用runner初始化chromedp实例
  22. // 实例在使用完毕后需要调用c.Shutdown()来释放资源
  23. c, err := chromedp.New(ctx, chromedp.WithRunner(run), chromedp.WithErrorf(dropChromeLogs))
  24. if err != nil {
  25. return nil, err
  26. }
  27.  
  28. return c, nil
  29. }
  1. ctx, cancel := context.WithCancel(context.Background())
  2. defer cancel()
  3. cdp := NewHeadless(ctx, "www.cnblogs.com")

下面是0.1.4版本的api:

  1. // 新版本中取消了cdp,将broeser对象和context合并在一起,方便了我们的操作
  2. func NewHeadless() (context.Context, context.CancelFunc) {
  3. opts := make([]chromedp.ExecAllocatorOption, 0)
  4. opts = append(opts, chromedp.ProxyServer("http://127.0.0.1:8118"))
  5. opts = append(opts, chromedp.Flag("headless", true))
  6. allocator, cancel := chromedp.NewAllocator(context.Background(), chromedp.WithExecAllocator(opts...)) return allocator, cancel
  7. }
  8. ctxt, cancel1 := NewHeadless()
  9. defer cance1l()
  10. c, cancel2 := chromedp.NewContext(ctxt)
  11. defer cancel2()

新版本中不会输出多余的log,同时也会默认启用headless模式。

如果你需要在新版本的chromedp启动实例时指定一个url,你可以这样做:

  1. broswer := chromedp.NewBroswer(c, startURL)
  2. 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:

  1. // chromedp.Sleep使headless chrome睡眠d表示的时间长度
  2. func Sleep(d time.Duration) Action
  3.  
  4. // chromedp.Navigate使浏览器访问参数给出的URL
  5. func Navigate(urlstr string) Action
  6.  
  7. // chromedp.SendKeys向指定的html元素内输入内容
  8. // sel是选择器字符串或是选择器要求的数据类型
  9. // opts指定使用何种选择器
  10. // 常用的选择器有:
  11. // chromedp.ByID:根据id来选择元素
  12. // chromedp.ByQuery:根据DOM.querySelector的规则选择元素
  13. func SendKeys(sel interface{}, v string, opts ...QueryOption) Action
  14.  
  15. // chromedp.Submit将指定的元素(通常是form)提交
  16. func Submit(sel interface{}, opts ...QueryOption) Action
  17.  
  18. // chromedp.WaitReady等待指定元素加载完毕
  19. func WaitReady(sel interface{}, opts ...QueryOption) Action
  20.  
  21. // chromedp.Click在指定元素上触发鼠标点击事件
  22. func Click(sel interface{}, opts ...QueryOption) Action
  23.  
  24. // chromedp.OuterHTML获取指定元素的HTML代码(包括其子元素)
  25. // html参数用于存放返回的HTML
  26. func OuterHTML(sel interface{}, html *string, opts ...QueryOption) Action

一个获取页面内容的小例子,更多例子在 https://github.com/chromedp/examples

  1. // 获取服务列表
  2. func GetServiceList(res *string) chromedp.Tasks {
  3. return chromedp.Tasks{
  4. // 访问服务列表
  5. chromedp.Navigate(ServiceListURL),
  6. // 等待直到body加载完毕
  7. chromedp.WaitReady("servicesList", chromedp.ByID),
  8. // 选择显示可用服务
  9. chromedp.Click("statusActive", chromedp.ByID),
  10. // 等待列表渲染
  11. chromedp.Sleep(2 * time.Second),
  12. // 获取获取服务列表HTML
  13. chromedp.OuterHTML("#servicesList table", res, chromedp.ByQuery),
  14. }
  15. }
  16.  
  17. var html string
  18. // cdp是chromedp实例
  19. // ctx是创建cdp时使用的context.Context
  20. err := cdp.Run(ctx, GetServiceList(&html) )
  21. if err != nil {
  22. // 错误处理
  23. }
  24.  
  25. // 成功取得HTML内容进行后续处理
  26. fmt.Println(html)

新版本的示例:

  1. var html string
  2. // ctxt是chromedp的实例,用于执行网页操作
  3. err := chromedp,Run(ctxt, GetServiceList(&html)) if err != nil { // error handle
  4. } // 成功取得数据
  5. fmt.Println(html)

另外新版本中关闭chrome实例的方式也有所不同:

  1. // 释放所有资源,并等待释放结束
  2. cancel2() // 官方给的是chromedp,FromContext(ctxt).Wait(),但是目前没有实现Wait方法 // 因此你可以像这样
  3. chromedp.FromContext(ctxt).Broswer.Shutdown()
  4. chromedp.FromContext(ctxt).Allocator,Wait()

因为目前新版本还很不稳定,所以推荐使用0.1.3版本。

至此golang通过chromedp( https://github.com/chromedp/chromedp )使用headless chrome进行动态网页的渲染和操作就介绍完了。

希望这篇文章能给你带来帮助,如有错误之处,欢迎交流指正。

ft_authoradmin  ft_create_time2019-05-24 18:22
 ft_update_time2019-05-24 18:24