项目地址: https://github.com/go-rod/rod
感谢大家的反馈优化了不少 API,因为之前的帖子已经过时了,所以在这里重新发下帖子。近期主要优化了异常处理。基本覆盖了 puppeteer 的主要功能,且支持更多便利的高级功能,比如等待 ajax,复杂代理,文件下载等。
这个库的主要原理就是利用浏览器的 devtools 可编程接口来操控浏览器。日常你可以用它自动化一些重复的页面任务,相比油猴子有更多的高级功能和 golang 强大的生态。工作上可以用它辅助集成化测试减少人力消耗。开发上可以用它简化一些传统爬虫需要费很多时间逆向工程的场景。
欢迎对自动化有兴趣的朋友加入到项目中来,这是我们的聊天室: https://discord.gg/PAaMGJw
可以通过示例文件大致了解 rod 的一些基本用法: https://github.com/go-rod/rod/blob/master/examples_test.go
下图是 rod,puppeteer 和 chromedp 实现相同输出的代码对比:
更多对比请移步: https://github.com/go-rod/rod/tree/master/lib/examples/compare-chromedp
关于 Must 前缀: https://github.com/go-rod/rod/blob/03b1d54362f72599d3bfa465aee6b48c9c51599b/examples_test.go#L137-L186
1
ysmood OP 比如这两天我将用 rod 每小时自动置顶一次这个帖子,示例项目: https://github.com/go-rod/v2ex-example
|
2
playniuniu 2020-09-21 09:38:29 +08:00 via Android
非常好用 支持一下
|
3
playniuniu 2020-09-21 09:40:13 +08:00 via Android 1
另外 能否出一个详细一点的文档 直接看 example test 代码还是有点费劲
|
4
supermoonie 2020-09-21 09:40:39 +08:00 via iPhone
需要多少线程支持一个 tab ?
|
5
ysmood OP @supermoonie 好奇你这个问题的出发点是啥。要在单核 CPU 上跑吗?理论上用了 Go 一个线程就够了 (green threads),不是很清楚该如何回答你这个问题。
|
6
ysmood OP @playniuniu 确实,目前还没有教程,感觉最近可能会花点时间出点教程,比如录个视频或者直播之类的会对我来说负担小点,写文章比较费精力。确实是想收集大家的想法才发帖子的,有什么具体想学的吗?
|
7
playniuniu 2020-09-21 10:00:09 +08:00 via Android
如果有可能 能否找几个网站 从最简单的 解析网页表单 ,操作鼠标点击 到比较复杂的过滑动验证码 eval js 等等 还有比较骚操作的 hijack 请求 bypass 这些 每一个能给个案例并讲解一下 rod 实现原理就好了
|
8
ysmood OP @playniuniu 好的,应该问题不大。还会讲解下如何利用 rod 调试复杂场景,这个也是很多人常常问的,这种就很难写文章,不录屏幕太麻烦解释了。
|
9
xwhxbg 2020-09-21 10:12:44 +08:00
是我火星了还是 promise 不支持 timeout 了? Pupeteer 不支持 abort slow operation,这个不是用 promise timeout 封装一下就行了么?
|
10
ysmood OP @xwhxbg 我是自己实现过 promise 库的,研究了很多 nodejs 底层工作原理的: https://github.com/ysmood/yaku
这说法其实是对比于 Go 的 context 特性来说的。大致就是指,比如一个 operation 要耗费一个小时,如果你用 nodejs 的 promise,实际上这个 operation 相关的资源还是驻留在内存的,并没有 abort 释放,只有到一个小时后才会释放。比如 cdp 的通信,由于是基于 websocket 的 frame,puppeteer 并没有实现原子级别的 abort 。你要想释放单个请求只能断开整个 websocket 链接,而 rod 这种就不用了,可以很原子的操作细节。 |
11
ysmood OP |
12
xwhxbg 2020-09-21 10:44:50 +08:00
@ysmood 呃,大家不都是用 bluebird 家的 promise 实现么? ES6 的确实没法 cancel,但是 bluebird 家的有 abort 之类的
|
13
daizige 2020-09-21 10:46:48 +08:00
持续关注中 感觉会很有意思
|
14
ysmood OP @xwhxbg 所以你得去了解 abort 的底层机制,bluebird 也没法解决我说的问题,它的 abort 对我来说都是虚假的 abort,实际还是在后台消耗资源。
|
15
ysmood OP @xwhxbg golang 贯穿整个系统底层的 context 概念是 nodejs 是很难比拟的: https://golang.org/pkg/context/
Promise 设计之初压根就没好好考虑 abort,草案直接放弃了这个方向的探寻。 写 puppeteer 大量重复的 async await 反复横跳于代码间。有时候写漏了 await 还不容易察觉。我个人觉得严重破坏了用 IDE 自动点点点补全的流畅感。 |
16
xwhxbg 2020-09-21 11:13:17 +08:00
@ysmood 我实在是没看懂你说的“底层机制”是什么,如果想要 cancel 请求,es6 就有 abort,如果想要 then 里面直接跳过,立刻回收内存,bluebird 也能实现
|
17
xwhxbg 2020-09-21 11:22:48 +08:00
@ysmood context 只是一个 handle 而已啊,贯穿底层是指什么呢? promise 传一个 object 当 handle 也是可以的啊。
golang 的 cancel 就是很正常的一个 handle,文档也说了`A CancelFunc does not wait for the work to stop. `说明即使调用了 cancel 也不能让代码立刻结束 可以描述一下你想要的 cancel 是什么效果吗? |
18
ysmood OP @xwhxbg 这个 google 一下类似关键字都可以出结果: https://stackoverflow.com/questions/29478751/cancel-a-vanilla-ecmascript-6-promise-chain
你看这个人说 bluebird 可以,然后评论的人就说的很清楚了: this definition of canceled is just rejecting the promise. it depends on the definition of "canceled" This is partially right, but if you have long promise chain, this approach would not work. 我觉挺清楚了啊,各种人都讨论过这个问题。 你说 ”es6 就有 abort“,能给个 ES6 官方的 spec 链接 吗?我好奇啥时候出的 |
19
xwhxbg 2020-09-21 11:35:44 +08:00
@ysmood https://stackoverflow.com/questions/31061838/how-do-i-cancel-an-http-fetch-request
我不太理解,即使没有 bluebird,写一个带 handle 的 cancel 也是很容易的事情 ```javascript let handle = { cancel: false}; let result = dosomething(handle).then(handle=>{ if (handle.cancel){ throw("cancel error"); } }); ... function cancel(){ handle.cancel = true; } ``` |
20
supermoonie 2020-09-21 11:37:55 +08:00 via iPhone
@ysmood 用 java 实现过这个协议,因为没有用异步 io,websocket 发送一个线程,接收一个线程,事件处理也是一个线程池在支持,所以问下 go 实现的话,是不是像 node 那样基于事件处理?
|
21
ysmood OP @xwhxbg abort 是不可能由外部独立完成的,必然是要 task 自己轮询是否有中断机信号,这个是计算机原理的基础。promise 是 task 自己返回一个承诺,而 golang context 是向 task 传递中断信号。这个根本差异很难用一些三方库抹平,必须要改很多库的接口方式。puppeteer 是没有实现这种细粒度中断信号控制的。所以我觉得这里我表达的应该还算合理。
|
22
ysmood OP @xwhxbg 所以问题的关键是 puppeteer 没给你接口让你传这个 cancel 的信号进去啊,这样说你觉得还有问题吗?
|
23
xwhxbg 2020-09-21 11:44:19 +08:00
@ysmood 你的意思是 golang 可以做到行级别的 cancel,能在一个 function 调用一半的时候不执行下半部分么?
|
24
ysmood OP @supermoonie 用的 goroutine 不是 libuv 那种。基于 go channel 的通信,不是 epoll,各有优劣。不会像 node 那样要各种侦听事件,写法基本是线性的,不会出现一大堆 callback
|
25
ysmood OP @xwhxbg 我刚不是说了计算机原理了吗? 当然是不可以的,这不是要对比 golang 和 nodejs,这里只是比较 rod 和 puppeteer 在处理 io block 的 cancel 上。rod 确实能更细力度的处理 cancel 信号,任何可以 io block 的地方基本都可以 cancel
|
26
ysmood OP @xwhxbg 比如 puppeteer 在模拟 click 时,会发送多个请求到 chrome,你没法阻止这几个请求只发出去一半。rod 可以。
|
27
supermoonie 2020-09-21 12:22:17 +08:00
@ysmood 那我换种问法,一个 Tab 需要多少个 goroutine 支持?我可能比较关注性能这块,因为 Chrome 本来就比较消耗资源😂
|
28
ysmood OP @supermoonie 相比 chrome,rod 的损耗可以忽略不计,就算你开 100 个 tab 也只有几个 goroutine,goroutine 数和 tab 没有关系,rod 使用了严格的 goleak 测试,极大的控制了 goroutine 的数量: https://github.com/uber-go/goleak
|
29
supermoonie 2020-09-21 13:57:25 +08:00 via iPhone
@ysmood 今天下班了我去看看你的源码,没有异步 IO 竟然可以做到只有几个 goroutine,很好奇是怎么做到的🌚
|
30
jzyzcz 2020-09-21 14:01:02 +08:00
学习一下...
最近也在用 puppeteer 做爬虫,感觉这个可玩性非常高 |
31
yejianmail 2020-09-21 18:46:08 +08:00 via Android
已⭐,想找个 Java 的 chrome cdp 没有找到合适的,selenium 用起来感觉不太爽
|
32
ysmood OP @supermoonie 欢迎来 review 代码。有任何问题都可以去聊天室: https://discord.gg/PAaMGJw
|
33
supermoonie 2020-09-21 18:56:39 +08:00 via iPhone
@yejianmail java 有 cdp4j,现在好像有社区版,不过代码写的不是太好,线程管理都没有
|
34
supermoonie 2020-09-21 18:59:20 +08:00 via iPhone
@ysmood review 不了,毕竟不是搞 go 的,准备抽时间,用 java nio 的封装库 netty 写一遍,之前用的阻塞 io,不是很满意
|
35
ysmood OP @yejianmail 和其他库的比较,里面有提到 selenium,可能对你有帮助: https://github.com/go-rod/rod#q-why-another-puppeteer-like-lib
|
36
ysmood OP @supermoonie 写通信异步处理这块算是这个库最小儿科的部分了。核心代码只有 200 行 https://github.com/go-rod/rod/blob/03b1d54/lib/cdp/client.go#L118
这个项目真正难的是如何在没有详细文档和资料的情况下用 cdp 接口完成一些高级功能,chrome 官方的文档非常糟糕,而且 cdp 接口设计本身也问题很大。比如单是模拟完美的 click 都够你折腾一阵子,看似简单的 click 需要调用约十个接口完成,chrome 只列举了全部接口,但是要调用哪 10 个接口完成像人一样点击鼠标是没有说的。 你要是感兴趣,go 挺简单的,比起你去研究 puppeteer,把这个项目翻译成 java 估计会更简单。 其实更好的做法不是用各种语言实现一遍相同的功能,而是基于 rod 这种库开发语言中性的服务,我们有个 issue 你可以看看: https://github.com/go-rod/rod/issues/21 我们也有个语言中性化的试验性项目 https://github.com/go-rod/wayang |
37
soseek 2020-09-21 20:09:40 +08:00 via Android
|
38
ysmood OP @soseek 这种项目挺多的 rod 也有相关 issue,我们是有计划要做这个的:
https://github.com/go-rod/rod/issues/139 https://github.com/go-rod/rod/issues/81 我是想指导个对这方面感兴趣的学生或者开发者来做个独立项目。实际项目能使用的范围不大,稍微复杂点的网站都很难录制。但是作为兴趣项目是非常有挑战价值的。 |
40
supermoonie 2020-09-21 22:58:29 +08:00
@ysmood emmm 。。。异步通信对于 go 可能简单,但是对于 Java 来说却不是小儿科,而且我觉得
https://chromedevtools.github.io/devtools-protocol/ 官方文档写的挺详细了,你是不是没有找对地方?其实两年前我就已经把 devtools-protocol 实现了 https://github.com/supermoonie/AutoChrome 只不过我觉得有些地方做的不够好,就搁置了,最近 Netty ( Java 异步 IO 库)学得差不多了,就想再用异步 IO 实现下。 其实 go 这方面做的挺好的,只是实在个人精力有限(个人项目在开发中。。。),要不然我也去搞搞 go 了,原本我还想用 Qt 去写一个 QAutoChrome 以及 Python 写 PyAutoChrome https://github.com/supermoonie/PyAutoChrome https://github.com/supermoonie/QAutoChrome 只不过没时间罢了,哈哈 |
41
ysmood OP @supermoonie 就是个经典生产消费模型而已,了解了原理,让我用 java 写我也觉得挺简单的,就算不用 netty 也可以的,瓶颈不在这儿。我觉得没必要争论这个,个人主观感受而已,仁者见仁智者见智。
https://chromedevtools.github.io/devtools-protocol/ 这个我是知道的,rod 文档里也多次提到了这个,而且 rod 本身很多代码也是自动生成于这个的。 我认识的深入开发过 cdp 的人都觉得 https://chromedevtools.github.io/devtools-protocol/ 写的不够详细,比如 Page.createIsolatedWorld 的文档,我是看不懂的,比如 IsolatedWorld 和 v8 的关系,生命周期,作用范围啥都没说。一堆类似的不明不白的 API 说明。 |
42
supermoonie 2020-09-22 00:19:47 +08:00 via iPhone
@ysmood 哈哈哈,这倒是,不过这些 api 无关紧要。突然想起来不打算搞 cdp 的一个重要原因,cef …… 前段时间已经打通了 cef 的 java 绑定,各种 api 随便调用,还实现了一个简单的浏览器,然后就觉得 cdp 不香了🤪
|
43
supermoonie 2020-09-22 00:22:12 +08:00 via iPhone
@ysmood 更正下,可能是我表达不准确,没有想表达哪种语言好与坏,我的锅🤣
|
44
ysmood OP @supermoonie cef 跟 cdp 要解决的问题完全不同。而且我要用 cef 的话,还用个毛的 java,直接 c++ 了,何必折腾自己呢。学个语言的成本比跨语言 embed 坑要少多了。
比如我可以立刻用同时用 puppeteer 和 rod 控制几千个 chromium 的集群,你用 cef 不又是要自己来一套 cdp 的轮子才能做到这种级别的解耦吗? |
45
iiusky 2020-09-22 21:45:59 +08:00 via Android
截图好像还是有问题 目前用的 pjs 代替
|
46
pucipuci 2020-09-22 21:50:53 +08:00 via Android
可以
|
48
ysmood OP |
49
lxilu 2020-09-22 23:31:27 +08:00 via iPhone
#21 操作系统原理软中断
|
50
wewewe 2020-09-23 19:52:46 +08:00
如果能够结合这个效果会更好
https://www.v2ex.com/t/706327 |
52
ysmood OP |
56
tikazyq 2020-09-29 10:14:50 +08:00
简单看了一下源码,貌似还是基于 Chrome 的吧,这个相当于是 puppeteer 的 golang 版本对吧
|
57
soulteary 2020-10-06 20:30:57 +08:00
@ysmood 你还在折腾这个呀,#38L 提到的那个东西,几年前就做完了,录制重放都问题不大,大概整了 7 ~ 8 套方案(有侵入、无侵入、移动、PC 、有客户端、无客户端、依赖框架、无框架依赖...),组合使用应该能解决绝大多数场景,尤其是功能层面 replay 。
当时郁闷的是不能从浏览器底层创建事件,做不到完美的仿真,现在可以做到了,但是却失去了再做一遍的兴趣和耐心。 |
58
ysmood OP @soulteary 我不是回复了吗?看 38 楼。你做的哪些基本没有实用价值啊,我大概是不会优先费时间在上面的,除非有感兴趣的人想去实现,我可以指导下。这种功能只能算 driver 的一个很小的应用而已。
|
59
lizhenda 2022-06-29 14:27:00 +08:00
准备试试水
|
60
shuirong1997 2023-05-26 14:48:29 +08:00
お問い合わせ内容お問い合わせ内容お問い合わせ内容お問い合わせ内容お問い合わせ内容お問い合わせ内容
|
61
yuanchao 2023-06-13 22:28:47 +08:00
赞,正好不满意 chromedp ,正在寻求替代方案,看到了这个项目,把文档 和 issue 都翻了一遍,已经准备在公司项目中使用了
|