天天看点

尝试自动重定向的次数太多_GoRod:基于DP协议的Web自动化和数据抓取工具

在日常开发中我们经常可能会遇到这样一些情景:需要使用一些自动化的手段来帮助我们测试;在获取某些网页的数据的时候,由于目标网页的数据是动态的,使用传统的数据获取手段无法有效的抓取;需要时不时的抓取某个网页保存为截图作为快照,以用于诸如竞品监测分析等;诸如以上的场景可能在以前我们比较难以实现,或者现有的工具或多或少存在着某些不足。今天就来介绍一款 Go 语言写的基于 DevTools Protocol 的工具,叫 Rod 。

Rod的Github地址:https://github.com/go-rod/rod

Rod 是什么?

Rod 是一款直接基于 DevTools Protocol 的高级 DevTools 驱动程序,它是为了网页自动化和抓取而设计的,Rod 还给用户暴露了一些底层接口,以便如果缺失某些功能的话,用户可以直接调用这些接口发送控制请求给浏览器。

Rod 能做什么?

既然是基于 DevTools Protocol 协议的,那基本 DevTools 能实现的功能大部分都能实现。比如浏览器页面控制、窗口控制、事件监听,根据选择器获取网页元素,模拟鼠标移动点击,页面事件监听、触发,网页注入脚本、样式表,网页内容修改,网页内容获取,执行脚本,Cookie 获取设置,页面截图,会话复用,页面监控等等功能。

Rod 的工作原理是什么?

Rod 在被调用后会尝试使用 WebSocket 去连接 DevTools,如果没有找到的话,会基于不同的系统尝试去一些默认的路径寻找本地浏览器,找到就打开,没有找到的话会尝试去下载一个再启动。

然后 Rod 使用 JSON-RPC 去和 DevTools 交互,以便控制浏览器。当需要控制某个具体的页面的时候,Rod 会给这个页面注入一个js的助手脚本,Rod 使用这个脚本来完成页面内容的获取,控制等。

其他同类工具存在什么问题?

chromedp 由于需要使用复杂的类 DSL 的任务来处理主逻辑,导致发生问题时很难通过理解代码来解决,也很难去追踪问题。chromedp 也无法处理 iframe,不支持影子 DOM,当程序崩溃时,会留下僵尸进程等。

selenium 由于是基于 webdriver protocol 的,功能上相对更少,比如没法处理关闭的影子 DOM,没办法保存网页为 PDF,没有对性能等工具的支持,由于依赖于浏览器驱动等也使得其变得难以使用和维护。虽然 selenium 宣称是支持跨浏览器的,但实际上在很多浏览器上都或多或少存在这样或那样的问题。

puppeteer 需要去处理复杂的 promise/async/await 问题,但在进行自动化测试的时候,更多想要同步的获取到状态之后再进行后续操作,对于 QA 来说不太友好。

cypress 对关闭的影子 DOM 限制很大,对跨域 iframe 来说也不太安全。

案例一:定时对某个网页截图

就像前面说的,有时候我们需要对某个竞品网页保持长期的关注,需要定时的保存快照来做分析,这时候我们就可以使用 rod 来方便的完成这个需求。

这里我写了一个小方法,每3秒(测试用,实际环境可以调整为半天、一天等频率)请求一次 gocn 这个网站,然后等页面加载完成后,将整个页面截图并按截图时间作为文件名保存到本地。

import (   "github.com/go-rod/rod"   "strconv"   "time")func monitorGocn() {   // 创建一个浏览器的实例   browser := rod.New().Connect()   url := "https://gocn.vip"   // 指定要获取的网页   page := browser.Page(url)   // 指定窗口大小,避免截图时截不完整   page.Window(0, 0, 1024, 768)   // 指定截图间隔时间,我这里是测试,就设置了3秒   interval := time.Duration(3) * time.Second   for {      select {      case          nowTimestamp := time.Now().Unix()         nowTimestampStr := strconv.Itoa(int(nowTimestamp))         // 重定向页面,后面的参数无实意,只是为了确保页面刷新         page.Navigate(url + "/?__t__=" + nowTimestampStr)         // 等待页面加载完成         wait := page.WaitRequestIdle()         wait()         file := "screenshot/" + nowTimestampStr + ".jpg"         // 整个页面截图并保存         page.ScreenshotFullPage(file)      }   }}
           

截图的结果:

尝试自动重定向的次数太多_GoRod:基于DP协议的Web自动化和数据抓取工具

截图效果如下:

尝试自动重定向的次数太多_GoRod:基于DP协议的Web自动化和数据抓取工具

案例二:自动签到某度贴吧

这个案例主要是以自动签到某度贴吧为例展示自动化操作,我们复用了用户的登录信息,设定一些需要登录的贴吧的数组,每天自动打开每个贴吧的首页,点击签到按钮进行签到。

对签到按钮进行元素审查,很容易得到对应的元素选择器。

尝试自动重定向的次数太多_GoRod:基于DP协议的Web自动化和数据抓取工具

然后我们编写代码:

import (   "github.com/go-rod/rod"   "github.com/go-rod/rod/lib/launcher"   "time")func signInBa() {   // 以NewUserMode形式启动,这样可以复用会话信息   // 当然你也可以以普通模式启动,自己调用程序登录   url := launcher.NewUserMode().Launch()   browser := rod.New().ControlURL(url).Connect()   // 先打开某度贴吧首页,方便复用   page := browser.Page("https://tieba.example.com/")   // 设定窗口大小   page.Window(0, 0, 1024, 768)   // 等待页面加载完成   wait := page.WaitRequestIdle()   wait()   // 这里是需要签到的贴吧的名称   kws := []string{"php", "java", "javascript"}   for _, kw := range kws {      // 组装贴吧地址      url := "https://tieba.example.com/f?kw=" + kw      // 前往某个贴吧首页      page.Navigate(url)      wait = page.WaitRequestIdle()      wait()      // 点击签到按钮      page.Element(".j_signbtn").Click()      // 休息10秒再继续      time.Sleep(time.Duration(10) * time.Second)   }}
           

执行后就可以看到如下效果:

尝试自动重定向的次数太多_GoRod:基于DP协议的Web自动化和数据抓取工具

以上两个案例也只是抛砖引玉,充分利用 Rod 能完成的功能远比这些强大,同时支持在 Docker 中启动浏览器,也就是说可以在服务器上去使用 Rod 的所有功能,写好的程序直接扔服务器上就可以等他定时执行,而且支持监控,也能自己处理错误来返回。

总之,用好 Rod 能使你的工作效率大大提升,也能为你完成各种定制化的自动化功能和高级抓取数据需求。