从名称中已经很清楚,无头浏览器是没有头的东西。 在前端环境中,它是开发人员必不可少的工具,您可以使用它来测试代码,检查质量和布局是否符合要求。 Frontend Conf的Vitaliy Slobodin认为有必要更深入地了解此工具的设备。
在Headless Chrome的主要组成部分和功能下,介绍了使用Headless Chrome的有趣场景。 关于Puppeteer的第二部分是一个方便的Node.js库,用于在Google Chrome和Chromium中管理Headless模式。
关于发言人: Vitaliy Slobodin-PhantomJS的前开发人员-将其关闭并掩埋的人。 有时,它有助于Konstantin Tokarev(
annulen )使用QtWebKit的“复活”版本-非常QtWebKit,其中支持ES6,Flexbox和许多其他现代标准。
Vitaliy喜欢在空闲时间探索浏览器,挖掘WebKit,Chrome等。 今天,我们将讨论浏览器,即无头浏览器及其整个幽灵系列。
什么是无头浏览器?
从名字开始,很明显这是没有头脑的。 在浏览器上下文中,这表示以下含义。
- 它没有内容的真实呈现 ,也就是说,它绘制了内存中的所有内容。
- 因此,它消耗的内存更少 ,因为不需要绘制图像或千兆字节的PNG,而人们尝试使用炸弹将它们放入后端。
- 它工作更快,因为它不需要在实际屏幕上呈现任何内容。
- 具有用于管理的编程接口 。 您问-他没有界面,按钮,窗口? 如何管理? 因此,它当然具有用于管理的界面。
- 一个重要的特性是能够在裸露的Linux服务器上安装 。 这是必要的,这样,如果您有全新安装的Ubuntu或Red Hat,则只需将二进制文件放入其中或将软件包放入其中,浏览器即可使用。 不需要萨满教或伏都教魔法。
这是典型的基于WebKit的浏览器。 您无法掌握这些组件-这只是一个视觉图像。

我们只对浏览器用户界面的顶部组件感兴趣。 这是相同的用户界面-窗口,菜单,弹出通知和其他所有内容。

这就是无头浏览器的外观。 注意区别吗? 我们完全删除了用户界面。 他不再。
仅浏览器保留 。
今天我们将讨论Headless Chrome()。 它们之间有什么区别? 实际上,Chrome是Chromium的品牌版本,具有专有的编解码器,相同的H.264,与Google服务的集成以及所有其他功能。 Chromium只是一个开源实现。

无头Chrome的诞生日期:2016年。 如果遇到他,您可以问我一个棘手的问题:“怎么回事,我还记得2017年的消息?” 事实上,早在2016年,来自Google的工程师团队就与PhantomJS开发人员联系,当时他们才刚刚开始在Chrome中实现Headless模式。 我们编写了整个Google Docks,以及如何实现该接口等。 然后,Google希望使接口与PhantomJS完全兼容。 直到那时,工程师团队才决定不做这种兼容性。
稍后,我们将讨论管理接口(API)(即Chrome DevTools协议),并了解如何使用它。
本文将以伪造金字塔的原理为基础(来自英语伪造者)。 选择了一个好名字-木偶戏是控制其他所有人的人!

金字塔的底部是无头铬-无头铬-这是什么?
无头铬

在中心-无头浏览器-相同的Chromium或Chrome(通常是Chromium)。 它具有所谓的渲染器(RENDERER)-绘制页面(您的窗口)内容的过程。 此外,每个标签页都需要有自己的渲染器,因此,如果您打开多个标签页,那么Chrome将启动许多渲染过程。
最重要的是您的应用程序。 如果我们使用Chromium或Headless Chrome,则Chrome或其他可嵌入其中的应用程序将位于其中。 最接近的类似物可以称为Steam。 所有人都知道,Steam本质上只是Steam网站的浏览器。 当然,他并非没有头脑,但与该计划相似。
有两种方法可以将无头Chrome嵌入(或使用)应用程序:
- 使用Puppeteer并使用Headless Chrome时为标准配置。
- 当您使用Headless库组件时,即实现无头模式并将其嵌入到您的应用程序中的库,例如,在C ++中。
您可能会问,为什么C ++在前端? 答案是DevTools C ++ API。 您可以通过多种方式实现和使用无头Chrome浏览器的功能。 如果使用Puppeteer,则将通过Web套接字与无头浏览器进行通信。 如果将Headless库嵌入桌面应用程序中,则将使用用C ++编写的本机接口。
但是除了所有这些,您还有其他事情,包括:
- 定制网络 -与网络交互的定制实现。 假设您在银行或政府机构中工作,该机构由三个字母组成,以“ F”开头,并使用浏览器不支持的非常棘手的身份验证或授权协议。 因此,您可能需要网络的自定义处理程序。 您可以简单地获取已经实施的库,然后在Chrome中使用它。
- Mojo模块 。 Mojo最接近的类似物是Node.js中的本地活页夹,它是用C ++编写的本地库。 Mojo所做的相同-您获取本机库,为其编写Mojo接口,然后可以在浏览器中调用本机库的方法。
铬成分

我再次听到一个棘手的问题:“为什么我需要这个可怕的计划? 我写了(插入您喜欢的框架的名称)。”
我相信开发人员应该知道他的工具如何工作。 如果您在React下写作,您应该知道React是如何工作的。 如果您使用Angular进行编写,则应该了解Angular的内幕。
因为在某些情况下,例如致命错误或生产中的非常严重的错误,您必须处理好胆量,然后才可以在那里,什么地方,如何以及如何迷路。 例如,如果您编写测试或使用Headless Chrome,您也可能会遇到一些奇怪的行为和错误。 因此,我将简要介绍一下铬具有哪些成分。 当您看到较大的堆栈跟踪时,您将完全知道挖掘的方式以及如何修复它。
平台层的最低级别。 其组成部分:
- Ozone是Chrome中的抽象窗口管理器,它与操作系统的窗口管理器进行交互。 在Linux上,它是X服务器或Wayland。 在Windows上,它是Windows窗口管理器。
- Scheduler是与之相同的调度程序,没有它,我们无处不在,因为我们都知道Chrome是一个多进程应用程序,并且我们需要以某种方式解析所有线程,进程以及其他所有内容。
- 网络 -浏览器应始终具有用于与网络配合使用的组件,例如,解析HTTP,创建标头,进行编辑等。
内容层是Chrome拥有的最大组件。 它包括:
- Blink是WebKit中基于WebCore的Web引擎。 它可以将HTML当作字符串,解析,执行JavaScript-就是这样。 他不再知道如何做:既不能使用网络,也不能画画-所有这些都发生在Blink之上。
闪烁包括:WebCore的高度修改版本-用于处理HTML和CSS的Web引擎; V8(JavaScript引擎); 以及我们在Chrome浏览器中使用的所有扩展程序的API,例如广告拦截器。 它还包括DevTools协议。
- Content API是一个接口,您可以使用它很容易地使用Web引擎的所有功能。 由于Blink内部有很多东西(可能有超过一百万个接口),为了不迷失所有这些方法和功能,您需要一个Content API。 输入HTML,引擎将自动对其进行处理,解析DOM,构建CSS OM,执行JavaScript,运行计时器,处理程序以及其他所有内容。
无头层级别-无头浏览器级别:
- 无头图书馆 。
- Embedder API接口,用于在应用程序中嵌入Headless库。
- 客户端API是Puppeteer使用的接口。
应用层应用层 :
- 您的应用程序( Embedding app );
- 小工具,例如Headless shell 。
现在让我们从更高的深度上升,激活-现在前端将运行。

Chrome DevTools协议
我们都遇到了Chrome DevTools协议,因为我们使用Chrome中的开发人员面板或远程调试器-相同的开发工具。 如果您远程运行开发人员工具,则使用DevTools协议与浏览器进行通信。 安装调试器时,请参见代码覆盖率,使用地理位置或其他方式-所有这些操作均由DevTools控制。

实际上,DevTools协议本身具有大量方法。 您的开发人员工具无权访问,其中可能有80%。 真的,您可以在那里做所有事情!
让我们看看该协议的全部含义。 实际上,这很简单。 它包含2个组件:
- DevTools目标-您正在检查的选项卡;
- DevTools客户端-假设这是一个远程启动的开发人员面板。

他们使用简单的JSON进行通信:
- 该命令有一个标识符,要执行的方法的名称以及一些参数。
- 我们发送请求并得到一个看起来非常简单的答案:所需的标识符,因为使用该协议执行的所有命令都是异步的。 为了使我们始终能够比较收到哪个团队的答复,我们需要一个标识符。
- 有结果。 在我们的例子中,它是一个具有以下属性的结果对象: 类型: “数字”, 值: 2, 描述: “ 2” ,未引发任何异常: wasThrown: false。
但除此之外,您的标签页还可以将事件发送回给您。 假设页面上发生事件或页面上发生异常时,您将通过此协议收到通知。


木偶戏
您可以使用自己喜欢的软件包管理器安装Puppeteer-不论是yarn,npm还是其他任何软件包。
使用它也很容易-只需在Node.js脚本中请求它,您就可以使用它了。

使用链接
https://try-puppeteer.appspot.com,您可以直接在网站上编写脚本,执行脚本并直接在浏览器中获取结果。 所有这些都将使用Headless Chrome来实现。
考虑一下Node.js下最简单的脚本:
const puppeteer = require('puppeteer'); (async() => { const browser = await puppeteer.launch() ; const page = await browser.newPage(); await page.goto('http://devconf.ru/') ; await page.emulateMedia('screen') ; await page.pdf({ path: './devconf.pdf, printBackground: true }); await browser.close() ; })();
在这里,我们只需打开页面并将其打印为PDF。 让我们实时查看此脚本的操作:
一切都会很酷,但是还不清楚里面有什么。 当然,我们有一个无头的浏览器,但看不到任何东西。 因此,Puppeteer具有一个称为headless的特殊标志:false:
const browser = await puppeteer.launch({ headless: false });
当您可以看到一些窗口并实时查看页面发生的情况(即脚本与页面的交互方式)时,需要以无头模式启动无头浏览器。

当我们添加此标志时,它将看起来相同的脚本。 浏览器窗口显示在左侧-更清楚。
木偶的优点:+这是无头Chrome的Node.js库。
+支持旧版本的Node.js> = 6。
+易于安装。
+用于管理整个巨型计算机的高级API。
无头Chrome易于安装,无需系统干预。 在第一次安装时,Puppeteer下载Chromium的版本,并将其直接安装在专门针对您的体系结构和OS的node_modules文件夹中。 您无需下载任何其他内容,它会自动执行此操作。 您还可以使用自己喜欢的Chrome版本,该版本已安装在系统上。 您也可以这样做-Puppeteer为您提供了这样的API。
不幸的是,如果仅进行基本安装,则也有缺点。
伪造者 :
-
没有顶级功能 :书签和密码的同步; 个人资料支持; 硬件加速等
-
软件渲染是最重要的减号。 所有计算和渲染都在您的CPU上进行。 但是在这里,Google工程师很快就会感到惊讶-实施硬件加速的工作已经在进行中。 如果您勇敢而勇敢,现在可以尝试使用它。
-直到最近,还没有对扩展的支持-现在就可以了! 如果您是一个狡猾的开发人员,则可以使用自己喜欢的AdBlock,指定Puppeteer的使用方式,所有广告都将被屏蔽。
-
不支持音频/视频 。 因为,为什么要选择无头浏览器音频和视频。
木偶可以做什么:还有一些很酷的东西,我将进一步介绍。
会话隔离
这是什么,它是和什么一起吃的,我们不会窒息吗? -不要cho!
会话隔离是
每个选项卡的
单独“存储库” 。 启动Puppeteer时,可以创建一个新页面,每个新页面都可以有自己的存储库,其中包括:
所有页面将彼此独立存在。 例如,这对于维持测试的原子性是必要的。
会话隔离
在启动并行会话时节省了资源和时间 。 假设您正在测试以开发模式构建的网站,即未最小化捆绑包,其重量为20 MB。 如果只想缓存它,则可以告诉Puppeteer使用所有已创建页面共有的缓存,并且该捆绑包将被缓存。
您可以
序列化会话以供以后使用 。 您编写了一个测试,以检查您网站上的特定操作。 但是您遇到了问题-该站点需要授权。 您不会在每次测试前都不断添加该站点上的授权。 Puppeteer允许您登录到该站点一次,然后在将来再次使用此会话。
虚拟计时器
您可能已经在使用虚拟计时器。 如果您在开发工具中移动了滑块以加快或减慢动画速度(当然,然后洗了手!),那么那时您在浏览器中使用了虚拟计时器。
浏览器可以使用虚拟计时器而不是真实计时器来
“滚动”时间,以加快页面加载或完成动画的速度。 假设您具有相同的测试,请转至主页,然后播放动画30秒钟。 任何时候都让测试等待对任何人都没有好处。 因此,您可以简单地加快动画的速度,以便在页面加载时立即完成动画,然后继续测试。
您可以
在网络请求运行时停止时间 。 例如,当到达后端的请求需要很长时间才能执行或返回错误时,您可以测试应用程序的响应。 您可以停止时间-Puppeteer允许。
在下面的幻灯片上,还有另一个选项:
停止并继续渲染器。 在实验模式下,可以告诉浏览器不要进行渲染,然后,如有必要,可以请求截图。 然后,无头的Chrome会快速渲染所有内容,提供屏幕截图,然后再次停止绘制任何内容。 不幸的是,开发人员已经设法更改了此API的工作原理,并且不再有此类功能。
下面的虚拟计时器的示意图。

最上面一行有两个常规计时器:第一个在第一个时间单位中启动,并以一个时间单位运行,第二个在第三个时间单位中启动,并以三个时间单位运行。
加速计时器-它们一个接一个地启动。 当我们暂停它们时,我们会等待一段时间,然后所有计时器开始计时。
以这个为例。 以下是一段截断的代码,它实际上只是从codepen.io加载动画页面并等待:
(async() => { const browser = await puppeteer.launch(); const page = await browser.newPage(); const url = 'https ://codepen.o/ajerez/full/EaEEOW/';
演示过程中的实现演示只是动画。
现在,使用Chrome DevTools协议,我们将发送一个名为Animation.setPlaybackRate的方法,并将值为12的playbackRate传递给它:
const url = 'https://codepen.o/ajerez/full/EaEEOW/';
我们加载了相同的链接,并且animashka开始工作得更快。 这是因为我们使用了虚拟计时器,并将动画的播放速度提高了12倍。
现在让我们做一个实验-传递playbackRate:0-看看会发生什么。 就是这样:根本没有动画,它不能播放。 零和负值只是完全暂停整个动画。
处理网络请求
您可以通过设置以下标志来
拦截网络请求 :
await page.setRequestlnterception(true);
在这种模式下,将出现一个附加事件,该事件在发送或接收网络请求时触发。
您可以随时
更改请求 。 这意味着您可以完全更改其所有内容(正文)及其标题,检查甚至取消请求。
为了
处理授权或身份验证 (包括通过HTTP的基本身份
验证) ,这是必需的。
您还可以进行
代码覆盖(JS / CSS) 。 使用Puppeteer,您可以自动化所有这些操作。 我们都知道可以加载页面,显示页面中使用了哪些类的实用程序。 但是我们对他们满意吗? 我认为不是。
浏览器更了解使用了哪些选择器和类-这是一个浏览器! 他总是知道执行哪个JavaScript,不执行,不使用CSS。
Chrome DevTools协议可助您一臂之力:
await Promise.all ( [ page.coverage.startJSCoverage(), page.coverage.startCSSCoverage() ]); await page.goto('https://example.com'); const [jsCoverage, cssCoverage] = await Promise,all([ page.coverage.stopJSCoverage(), page.coverage.stopCSSCoverage() ]):
在前两行中,我们启动了一个相对较新的功能,使您可以查找代码覆盖率。 运行JS和CSS,转到某个页面,然后说-停止-我们可以看到结果。 这些不是想象中的结果,而是浏览器由于引擎而看到的结果。
除其他外,已经有一个插件供Puppeteer将其全部导出到伊斯坦布尔。
在Puppeteer金字塔的顶部是您在Node.js上编写的脚本-就像教父一样,在所有底部。

但是……“在丹麦王国,并非一切都平静……”-正如莎士比亚写道。
无头浏览器有什么问题?
尽管无头浏览器的所有出色功能都可以完成很多工作,但它们仍然存在问题。
不同平台上页面渲染的差异
我真的很喜欢这个项目,并不断谈论它。 让我们看看这张照片。

这是一个带有纯文本的常规页面:右侧-在Linux上的Chrome中呈现,左侧-在Windows下。 那些使用屏幕快照进行测试的人都知道,始终会设置一个值,称为“错误裕度”,该值确定何时将屏幕快照视为相同,何时将其视为相同。
实际上,问题在于,无论您如何尝试设置此阈值,该错误将始终超出此范围,并且您仍将收到假阳性结果。 这是由于所有三个平台上的所有页面甚至Web字体都呈现不同的事实-在Windows上根据一种算法,在MacOS上不同,在Linux上通常是动物园。
您不能只接受并测试屏幕截图 。
您会说:“我只需要一台参考计算机,即可在其中运行所有这些测试并比较屏幕截图。” 但是实际上,这非常不方便,因为您必须等待CI,并且希望在计算机上本地检查是否损坏了某些东西。 如果您在Linux机器上拍摄了参考屏幕截图,并且您使用的是Mac,那么将会有错误的结果。
因此,我说完全不用屏幕截图进行测试-算了吧。
顺便说一句,如果您仍然想使用屏幕截图进行测试,Roman Dvornov的精彩文章“
使用屏幕截图进行单元测试:打破声音障碍 ”。 这是侦探小说。
锁具
许多大型内容提供商不喜欢您以非法方式抓取或获取其内容。 想象一下,我是一家主要的内容提供商,并希望与您一起玩同一游戏。 在两个不同的浏览器中有两个GET请求。

你能猜出Chrome在这里吗? 不接受“两个”选项-Chrome仅是其中之一。 最有可能,您将无法回答这个问题,作为主要的内容提供商,我可以:在右侧-PhantomJS,在左侧-Chrome。

通过匹配请求中HTTP标头的顺序,我可以检测到您的浏览器(确切地说是Chrome或FireFox)。 如果主机优先(我很清楚),那就是Chrome。 那我无法比较。 是的,当然,还有更复杂的算法-我们不仅检查顺序,还检查值等。 等 但是重要的是,我可以放弃您的标题,检查您的身份,然后才阻止您或不阻止您。
无法实现某些功能(Flash)
您是否曾经在浏览器中深入研究过直接使用Flash的硬盘? 我莫名其妙地看着-然后六个月没睡觉了。
我们都记得曾经有Flash时曾经观看YouTube的方式:视频旋转,一切都很好。 但是,当在诸如Flash之类的页面上创建嵌入式对象时,它总是向您的OS请求一个真实的窗口。 也就是说,除了浏览器窗口外,Flash YouTube窗口中还有操作系统的另一个窗口。 除非您提供一个真实的窗口-不仅是真实的窗口,还是在屏幕上可见的窗口,否则Flash无法工作。 因此,某些功能无法在无头浏览器(包括Flash)中实现。
全自动和机器人
就像我之前说的,大型内容提供商非常害怕当您编写仅窃取有偿提供的信息的蜘蛛或抓斗时。
使用了各种技巧。 有关如何仍然检测无头浏览器的文章。 我可以说
您将无法检测无头浏览器 。 此处描述的所有方法均被绕过。 例如,存在使用画布的检测方法。 我记得甚至有一个脚本看着鼠标在屏幕上移动并填充了画布。 我们是人,我们移动鼠标的速度相当慢,而无头Chrome的速度要快得多。 该脚本了解到Canvas填充得太快-这意味着它很可能是无头Chrome。 我们也对此进行了规避,只是放慢了浏览器的速度就没有问题。
没有标准(单个)API
如果您在其他浏览器(无论是Safari还是FireFox)中观看了无头实现,那么所有这些都可以使用webdriver API来实现。 Chrome具有Chrome DevTools协议。 在Edge中,没有任何东西是清晰的-什么在里面,什么不在。
WebGL?
人们还要求在无头模式下使用WebGL。 此
链接可让您访问Google Chrome浏览器错误跟踪器。 在那里,开发人员正在积极投票支持WebGL的无头模式的实现,并且他已经可以画点东西了。 现在,它们仅受硬件渲染的约束。 硬件渲染的实现完成后,WebGL将自动可用,也就是说,可以在后台完成某些操作。
但是,并非一切都那么糟糕!
我们在市场上有第二个参与者-2018年5月11日,有
消息称Microsoft在其Edge浏览器中决定实现与Google Chrome中几乎相同的协议。 他们专门创建了一个财团,在这里讨论要引入行业标准的协议,以便您可以获取脚本并在Edge,Chrome和FireFox下运行它。
但是只有一个“但是”-不幸的是,Microsoft Edge没有无头模式。 他们的投票结果是:“给我们一个无头的模式!” -但他们保持沉默。 大概看到了秘密。
TODO(结论)
我告诉所有这些,以便您可以去找经理,或者,如果您是经理,可以去开发人员,然后说:“就是这样!
我们不再需要硒-给我们伪娘! 我们将在其中进行测试。” 如果发生这种情况,我将很高兴。
但是,如果您可以像我一样学习使用Puppeteer的浏览器,主动发布错误或发送请求请求,那么我将感到更加高兴。 OpenSource中的该
工具位于GitHub上,是用Node.js编写的-您可以借用它并为它做贡献。
Puppeteer的情况很独特,因为有两个团队在Google工作:一个专门与Puppeteer交易,另一个与无头模式交易。 如果用户发现了一个错误并在GitHub上编写了该错误,那么如果该错误不是在Puppeteer中,而是在Headless Chrome中,则该错误将转至Headless Chrome命令。 如果他们在那里修复了问题,那么Puppeteer将会非常快速地更新。 当社区帮助改善浏览器时,这将形成一个单一的生态系统。
因此,我敦促您帮助改进该工具,该工具不仅会被您使用,还会被其他开发人员和测试人员使用。
联络资料:
- github.com/vitallium
- vk.com/vitallium
- twitter.com/vitalliumm
Frontend Conf Moscow-前端开发人员专业会议将于10月4日至5日在莫斯科 Infospace举行。 会议网站上已经发布了接受报告的列表 。
在我们的时事通讯中,我们会定期对演讲进行主题审查,谈论已发布的成绩单和未来的事件- 报名首先接收新闻。
这是指向我们YouTube前端频道的链接,其中包含与项目客户部分的开发有关的所有演讲。