auto-chrome
使用Node.js自动化控制Chrome或Chromium,基于chrome devtools协议的高仿真用户行为模拟器。
借鉴于puppeteer,选择重写是因为在实际应用中puppeteer存在各种奇怪的bug,导致线程阻塞且难以被修复。另外,puppeteer不会支持所有chrome devtools协议,同时又缺乏可扩展性和易用性,一些实现细节也不符合预期。
chrome devtools协议api过于原始,对开发者而言并不友好。puppeteer api在设计上大量延用了chrome devtools协议的原始风格,虽然功能丰富,但是易用性却很糟糕。
auto-chrome以简洁和易用为设计原则,重点简化常见应用场景,只提供常用功能,通过扩展的方式满足定制化需求。
项目中部分引用了puppeteer源码,目前依然处于初级阶段,很多功能并不完善。
特性
-
自动切换标签,根据当前活跃标签自动聚焦,通过chrome.page获取当前处于激活状态的标签。这在实际应用中避免了多标签切换带来的苦恼,同时减少手动切换标签导致的混乱。
-
解除鼠标、键盘、触控输入设备与页面的绑定,避免频繁在多个页面间切换设备。
-
支持高仿真输入,模拟鼠标移动轨迹和Touch手势。
-
简化的错误处理机制,即使任务出现异常依然能继续运行,尽可能保障线程不出现持久性阻塞。
-
消息超时机制,由于各种难以预料的情况,devtools并不保证100%对接收的消息做出回应,在没有超时机制的状态下消息将处于持续等待状态,导致任务无法继续执行。
Install
npm install auto-chrome
chromium安装
由于网络环境因素,auto-chrome并没有像puppeteer那样直接将chromium作为npm依赖进行安装。因此你需要手动下载chromium,并在launch.executablePath配置项中指定安装路径。
推荐源:https://npm.taobao.org/mirrors/chromium-browser-snapshots/
chrome devtools术语
-
Target
表示浏览器中的多种对象,可以是browser、page、iframe、other类型其中之一。当type为page类型时,targetId对应于主框架的frame id。
-
Session
session机制用于创建多个会话,可以为每个Target绑定独立的session,也可以让多个Target共享同一个session。
-
Page
浏览器标签,Chrome中允许打开多个Page,但始终只有一个Page处于激活状态。
-
Runtime
JavaScript运行时,用于向网页注入JS代码实现对DOM的操作。
-
Frame
网页中的框架,主Frame中允许包含多个子Frame。
-
Context
JavaScript运行时所处的的上下文,由于页面内可能包含Frame,每个Frame拥有独立的运行时,因此需要生成唯一contextId来区分它们。
API
class: autoChrome(options)
chrome.mouse
鼠标操作,page.mouse的快捷引用
chrome.keyboard
鼠标操作,page.keyboard的快捷引用
chrome.touch
触控设备操作,page.touch的快捷引用
chrome.pages
包含所有打开page的Map对象
chrome.page
当前激活状态的page
chrome.newPage(url)
url
String 打开网页地址,缺省时打开空白网页
chrome.closePage(pageId)
chrome.send(method, params)
发送原始的chrome devtools协议消息
-
method
String 方法名
-
params
Object 参数
chrome.close()
关闭浏览器
class: Page
page.mouse
鼠标实例
page.keyboard
键盘实例
page.touch
触控设备实例
page.emulate(options)
设备仿真,直接调用该方法可能导致混乱,正常应该由事件驱动在创建标签执行page.emulate(),手动调用会存在延时覆盖问题。
-
options
Object 选项
-
mobile
Boolean 移动设备
-
hasTouch
Boolean 启用触控
-
width
Number 屏幕宽度
-
width
Number 屏幕高度
-
geolocation
Object 地理位置
-
longitude
Number 经度
-
latitude
Number 纬度
-
accuracy
Number 精准度
page.goto(url)
在标签内打开新网页
page.evaluate(pageFunction, arg, arg, ...)
向页面注入js函数,获取执行后的返回值
page.$(selector)
选择单个元素
-
selector
String CSS选择器
-
return
Object 单个Elment实例
page.$$(selector)
选择多个元素
page.click(selector)
通过CSS选择器点击元素
page.type(selector, text, options)
通过CSS选择器聚焦input,输入文本
-
selector
String CSS选择器
-
text
String 输入文本
-
options
Object 配置信息
page.scroll(selector)
滚动至指定元素可视区域,会尽量沿Y轴居中
page.send(method, params)
发送包含session的原始chrome devtools协议消息
-
method
String 方法名
-
params
Object 参数
page.touchScroll(selector, options)
通过touch方式,滚动页面至指定元素可视区
page.focus(selector)
通过CSS选择器聚焦元素
page.getBoundingRect(selector)
通过CSS选择器获取元素坐标,值由getBoundingClientRect()函数获取
page.close()
关闭标签
class: Element
用于实现可追溯的远程elment,避免代码重复提交和重复执行。
对于大的对象或DOM对象,直接返回它们并不现实,因此需要一种远程操作的增量机制。devtools通过保存注入函数的执行结果并返回引用id,实现状态追踪,这样就可以在已有远程结果基于上做增量操作。
elment.$(selector)
-
selector
String
-
return
Object Elment实例
选择单个元素,并生成远程引用对象
elment.$$(selector)
选择多个元素,并生成远程引用对象
elment.get(name)
获取elment中指定的属性值
elment.set(name, value)
-
name
String 属性名称
-
value
* 属性值
设置elment中指定的属性值
elment.value(value)
获取或设置值,仅适用于表单元素
elment.focus()
聚焦元素
elment.getBoundingRect()
通过getBoundingClientRect函数获取元素大小、坐标信息
class: Mouse
mouse.click(x, y, options)
新增模拟鼠标移动轨迹,原click可能出于效率考虑,只会触发一次mousemoved
click操作中已经包含了move,多数情况下不再需要单独模拟move操作,除非只移动鼠标而不需要点击
-
options
Object
steps
Number mousemoved事件的触发次数,默认20
mouse.move(x, y, options)
将steps默认值改为20,原值为1,即只触发一次。移动距离相同时,触发次数越少,对应的移动速度越快
-
options
Object 选项
steps
Number 触发mousemoved事件的次数,默认值20
mouse.scroll(x, y, step)
滚动至指定坐标,目前仅支持纵向滚动
-
x
Number 横向坐标,0
-
y
Number 纵向坐标
-
step
Number 步长
class: Touch
touch.slide({start, end, steps})
模拟touch单点滑动手势
touch.scroll(x, y, options)
通过touch滚动页面至指定的可视坐标
-
x
Number 目标x坐标
-
y
Number 目标y坐标
-
options
Object
interval
Number 连续滑动的时间间隔,默认2000,单位ms