JavaScript 实现网页截屏五种方法
JavaScript 实现网页截屏五种方法
最近研究了下如何利用JavaScript实现网页截屏,包括在浏览器运行的JS,以及在后台运行的nodeJs的方法。主要看了以下几个:
- PhantomJS
- Puppeteer(chrome headless)
- SlimerJS
- dom-to-image
- html2canvas
测试的网页使用了WebGL技术,所以下面的总结会和WebGL相关。
名词定义
headless browser
无界面浏览器,多用于网页自动化测试、网页截屏、网页的网络监控等。
PhantomJS
PhantomJS是可以通过JS进行编程的headless浏览器,使用的是QtWebKit内核。
实现截屏的代码,假设文件名为github.js:
// 创建一个网页实例
var page = require( webpage ).create();
// 加载页面
page.open( http://github.com/ , function () {// 给网页截屏,保存到github.png文件中page.render( github.png );phantom.exit();
})
运行:
phantomjs github.js
普通的页面没有问题,但是如果运行包含WebGL的页面,发现截屏不对。经过一些调查,发现不支持WebGL,github issue。
总结:
1、PhantomJs已经停止维护了,所以不太建议继续使用。停止维护的一个原因是chrome发布的headless版本对它造成了一定冲击。
2、不支持WebGL。但是,还是有开发者说可以自己给PhantomJS添加WebGL支持,不过,这个方案目前超出我的知识范围了,就没有继续研究。
Puppeteer(chrome headless)
Puppeteer是一个Node库,提供了控制chrome和chromium的API。默认运行headless模式,也支持界面运行。
实现截屏的代码example.js:
const puppeteer = require( puppeteer );(async () => {const browser = await puppeteer.launch();const page = await browser.newPage();await page.setViewport({ // 设置视窗大小width: 600,height: 800});await page.goto( https://example.com ); // 打开页面await page.screenshot({path: example.png }); // path: 截屏文件保存路径await browser.close();
})();
运行:
node example.js
接下来看下screenshot方法的实现原理:
screenshot的源码位于lib/cjs/puppeteer/common/Page.js文件中,是一个异步方法:
async screenshot(options = {}) {// ...return this._screenshotTaskQueue.postTask(() => this._screenshotTask(screenshotType, options));
}
async _screenshotTask(format, options) {// ...const result = await this._client.send( Page.captureScreenshot , {format,quality: options.quality,clip,});// ...
}
这个this._client.send又是个什么东西?别急,我们重新看下Puppeteer的定义:
“ Puppeteer is a Node library which provides a high-level API to control Chrome or Chromium over the DevTools Protocol.
” 看到最后面那个DevTools Protocol了吗?这是个什么东西:
“ The Chrome DevTools Protocol allows for tools to instrument, inspect, debug and profile Chromium, Chrome and other Blink-based browsers.
” 详细的解释可以看这篇博客。
简单来说,Puppeteer就是通过WebSocket给浏览器发送遵循Chrome Devtools Protocol的数据,命令浏览器去执行一些操作。然后,浏览器再通过WebSocket把结果返回给Puppeteer。这个过程是异步的,所以看源代码会发现好多async/await。
所以screenshot方法是调用了Chrome Devtools Protocol的captureScreenshot。
总结:
- 支持WebGL。
- 网页比较复杂的话,截屏时间也挺长的,我测试的页面是几百毫秒。
- Puppeteer是对(CDP)Chrome Devtools Protocol功能的封装。大部分功能都是通过WebSocket传输给CDP处理的。
SlimerJS
SlimerJS和PhantomJS类似。不同点是SlimerJS是基于火狐的浏览器引擎Gecko,而不是Webkit。
SlimerJS可以通过npm安装,最新版本是1.x。不过兼容的火狐版本是53.0到59.0。我看现在火狐最新版本都82了。
因为我本机是安装了火狐最新版本的,所以我还得安装一个老版本的火狐,比如59.0。可以参考这篇安装旧版本的火狐浏览器。我是mac系统,感觉安装还是挺容易的。
实现截屏的代码screenshot.js:
var page = require( webpage ).create();
page.open("http://slimerjs.org", function (status) {page.viewportSize = { width:1024, height:768 };page.render( screenshot.png );
});
运行
// mac操作系统设置火狐路径
export SLIMERJSLAUNCHER=/Applications/Firefox.app/Contents/MacOS/firefox
./node_modules/.bin/slimerjs screenshot.js // 我是局部安装的slimer包
需要注意的是SLIMERJSLAUNCHER=/Applications/Firefox.app/Contents/MacOS/firefox启动的是火狐默认的安装路径,因为我一开始就有火狐浏览器,所以启动的是最新版本的浏览器,然后就报错了,说不兼容。在前面我安装过一个59版本的火狐,那么这个火狐浏览器的路径是什么?
在应用程序里面我把这个旧版本的火狐命名为Firefox59,然后这个路径就是/Applications/Firefox59.app/Contents/MacOS/firefox。重新设置SLIMERJSLAUNCHER为59版本的火狐浏览器之后,发现就能成功了。
不过,Puppeteer默认会打开浏览器界面,也就是non-headless模式。如果要使用headless模式,可以
./node_modules/.bin/slimerjs --headless screenshot.js
不过,headless模式下,不支持WebGL。
我在写例子的时候,发现的一个明显的不同就是Puppeteer截屏是异步函数,而SlimerJS截屏是同步函数?好奇心驱使下,看了下源码(src/modules/slimer-sdk/webpage.js):
render: function(filename, options) {// ...let canvas = webpageUtils.getScreenshotCanvas(browser.contentWindow,finalOptions.ratio,finalOptions.onlyViewport, this);}canvas.toBlob(function(blob) {let reader = new browser.contentWindow.FileReader();reader.onloadend = function() {content = reader.result;}reader.readAsBinaryString(blob);}, finalOptions.contentType, finalOptions.quality);// ...
}
webpageUtils.getScreenshotCanvas(src/modules/webpageUtils.jsm):
getScreenshotCanvas : function(window, ratio, onlyViewport, webpage) {// ...// create the canvaslet canvas = window.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");canvas.width = canvasWidth;canvas.height = canvasHeight;let ctx = canvas.getContext("2d");ctx.scale(ratio, ratio);ctx.drawWindow(window, clip.left, clip.top, clip.width, clip.height, "rgba(0,0,0,0)");ctx.restore();return canvas;
}
关键代码就是那行ctx.drawWindow。what?JS原生API还支持直接截屏?
CanvasRenderingContext2D.drawWindow():只有火狐支持,已经被废弃掉的非规范定义的标准API。
总结
- 1.0版本支持的火狐版本是53.0到59.0。不保证最新版本火狐可用。
- headless模式下,不支持WebGL。
dom-to-image
dom-to-image:前端截屏的开源库。工作原理是:
SVG的foreignObject标签可以包裹任意的html内容。那么,为了渲染一个节点,主要进行了以下步骤:
- 递归地拷贝原始dom节点和后代节点;
- 把原始节点以及后代节点的样式递归的应用到对应的拷贝后的节点和后代节点上;
- 字体处理;
- 图片处理;
- 序列化拷贝后的节点,把它插入到foreignObject里面,然后组成一个svg,然后生成一个data URL;
- 如果想得到PNG内容或原始像素值,可以先使用data URL创建一个图片,使用一个离屏canvas渲染这张图片,然后从canvas中获取想要的数据。
测试的时候,发现外部资源不能加载,所以简单的了解了后就放弃了。
html2canvas
html2canvas。网上查了下感觉有一篇文章写的挺好的:浅析 js 实现网页截图的两种方式。感兴趣的可以看下。
未验证的猜想
虽然后面这两种是前端的实现方式,但是结合前面讲的headless库,也是可以实现后端截屏的。以Puppeteer的API为例,可以首先使用page.addScriptTag(options)往网页中添加前端截屏的库,然后在page.evaluate(pageFunction[, …args])中的pageFunction函数里面写相应的截屏代码就可以了,因为pageFunction的执行上下文是网页上下文,所以可以获取到document等对象。
- End -
JavaScript 实现网页截屏五种方法相关推荐
- JS 实现网页截屏五种方法
最近研究了下如何利用JavaScript实现网页截屏,包括在浏览器运行的JS,以及在后台运行的nodeJs的方法.主要看了以下几个: PhantomJS Puppeteer(chrome headle ...
- JavaScript实现网页截屏的5种方法(详解+代码)
最近研究了下如何利用JavaScript实现网页截屏,包括在浏览器运行的JS,以及在后台运行的nodeJs的方法.主要看了以下几个: PhantomJS Puppeteer(chrome headle ...
- JavaScript实现网页截屏方法总结
" 关注『前端开发博客』公众号,回复 加群 " 最近研究了下如何利用JavaScript实现网页截屏,包括在浏览器运行的JS,以及在后台运行的nodeJs的方法.主要看了以下几个: ...
- JavaScript 侦测手机浏览器的五种方法
一.navigator.userAgent 最简单的方法就是分析浏览器的 user agent 字符串,它包含了设备信息. JS 通过navigator.userAgent属性拿到这个字符串, ...
- Vb自动读取本地HTML,VB获取网页源代码的五种方法
方法1:inet控件调用方法 Inet1.OpenURL 添加microsoft ineternet transfor conctrol6.0 控件 方法2:XMLHTTP '如果出现乱码,UTF-8 ...
- 【JS】1007- JavaScript实现网页截屏的5种方法
作者:luckness 原文:https://segmentfault.com/a/1190000037673677 最近研究了下如何利用JavaScript实现网页截屏,包括在浏览器运行的JS,以及 ...
- C#后台调用前台javascript的五种方法
第一种,OnClientClick (vs2003不支持这个方法) <asp:Button ID="Button1" runat="server" ...
- JavaScript 数组遍历的五种方法(转)
转自:JavaScript 数组遍历的五种方法 这篇文章主要介绍了JavaScript 数组遍历的五种方法,帮助大家更好的理解和学习使用JavaScript,感兴趣的朋友可以了解下 在使用 JavaS ...
- JavaScript数组去重的五种方法
JavaScript数组去重的五种方法 先简单准备一个数组,用于方法的实验: let array = [1,1,2,3,4,4,1,5,6,6,7,7,7]; console.log(`去重前的数组: ...
最新文章
- 百度之星2014资格赛 1004 - Labyrinth
- 52Exchange 2010升级到Exchange 2013-升级SH站点Ex2010到2013
- Lingo计算最优解
- C#读取数据库返回泛型集合(DataSetToList)
- filename.whl is not supported wheel on this platform 部分第三方库无法安装的解决办法
- 002-JavaScript基本应用
- Unity与安卓开发的一些路径知识
- DSP技术是利用计算机或,什么是dsp技术?dsp技术有哪些应用?
- Vue安装教程(保姆级详细教程)
- MacOS使用GeoIP2库查询ip详细信息
- Python:SEIR传染病模型
- 博客园博客排版(js样式实例)
- IE浏览器无法打开HTTPS解决办法
- html实现银行卡号输入,Vue中Element-ui 输入银行账号每四位加一个空格的实现代码_飛雲_前端开发者...
- 根据股票涨跌用KMeans进行分类
- android打电话的intent,如何在Android中使用intent打电话?
- 360企业安全校招内推(可免笔试)
- 八月暑期福利,10本Python热门书籍免费送!
- mac格式化系统恢复|mac格式化后怎么恢复?
- Dreamweaver制作简易的拼图游戏教程
热门文章
- 【GPS周-周内秒、BDS周-周内秒转换与逆转换】
- Client<unknown> disconnected, not authorised
- MQTT入门2 -- “Error: Invalid password hash for user nick.”和“Connection Refused: not authorised.”...
- 在 Airbnb 使用机器学习预测房源的价格
- 黑客入侵16进制密码_密码与密码黑客如何诱骗您入侵您的详细信息
- 科沃斯扫地机器人抓不转_科沃斯扫地机器人一边转一边不转什么原因 科沃斯扫地机器人常见...
- 智能红外遥控器(三):红外学习温湿度读取
- python调用百度api实现摄像头识别文字
- java 随机生成常用汉字_Java代码实现随机生成汉字的方法
- HashMap和HashTable的异同点