热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

JavaScript实现网页截屏的5种方法(附代码)

作者:luckness原文:https:segmentfault.coma1190000037673677最近研究了下如何利用JavaScript实现网

作者:luckness

原文:https://segmentfault.com/a/1190000037673677

最近研究了下如何利用Javascript实现网页截屏,包括在浏览器运行的JS,以及在后台运行的nodeJs的方法。主要看了以下几个:

  1. PhantomJS

  2. Puppeteer(chrome headless)

  3. SlimerJS

  4. dom-to-image

  5. html2canvas

测试的网页使用了WebGL技术,所以下面的总结会和WebGL相关。

1名词定义

2headless browser

无界面浏览器,多用于网页自动化测试、网页截屏、网页的网络监控等。

3PhantomJS

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支持,不过,这个方案目前超出我的知识范围了,就没有继续研究。

4Puppeteer(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。

总结:

  1. 支持WebGL。

  2. 网页比较复杂的话,截屏时间也挺长的,我测试的页面是几百毫秒。

  3. Puppeteer是对(CDP)Chrome Devtools Protocol功能的封装。大部分功能都是通过WebSocket传输给CDP处理的。

欢迎关注公众号:前端印象

5SlimerJS

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. 1.0版本支持的火狐版本是53.0到59.0。不保证最新版本火狐可用。

  2. headless模式下,不支持WebGL。

6dom-to-image

dom-to-image:前端截屏的开源库。工作原理是:SVG的foreignObject标签可以包裹任意的html内容。那么,为了渲染一个节点,主要进行了以下步骤:

  1. 递归地拷贝原始dom节点和后代节点;

  2. 把原始节点以及后代节点的样式递归的应用到对应的拷贝后的节点和后代节点上;

  3. 字体处理;

  4. 图片处理;

  5. 序列化拷贝后的节点,把它插入到foreignObject里面,然后组成一个svg,然后生成一个data URL;

  6. 如果想得到PNG内容或原始像素值,可以先使用data URL创建一个图片,使用一个离屏canvas渲染这张图片,然后从canvas中获取想要的数据。

测试的时候,发现外部资源不能加载,所以简单的了解了后就放弃了。

7html2canvas

html2canvas。网上查了下感觉有一篇文章写的挺好的:浅析 js 实现网页截图的两种方式。感兴趣的可以看下。

8未验证的猜想

虽然后面这两种是前端的实现方式,但是结合前面讲的headless库,也是可以实现后端截屏的。以Puppeteer的API为例,可以首先使用page.addScriptTag(options)往网页中添加前端截屏的库,然后在page.evaluate(pageFunction[, ...args])中的pageFunction函数里面写相应的截屏代码就可以了,因为pageFunction的执行上下文是网页上下文,所以可以获取到document等对象。

最后

  1. 感谢阅读,欢迎分享给身边的朋友,

  2. 记得关注噢,黑叔带你飞!

亲,点这涨工资 


推荐阅读
  • 本文介绍了使用AJAX的POST请求实现数据修改功能的方法。通过ajax-post技术,可以实现在输入某个id后,通过ajax技术调用post.jsp修改具有该id记录的姓名的值。文章还提到了AJAX的概念和作用,以及使用async参数和open()方法的注意事项。同时强调了不推荐使用async=false的情况,并解释了JavaScript等待服务器响应的机制。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文总结了Java中日期格式化的常用方法,并给出了示例代码。通过使用SimpleDateFormat类和jstl fmt标签库,可以实现日期的格式化和显示。在页面中添加相应的标签库引用后,可以使用不同的日期格式化样式来显示当前年份和月份。该文提供了详细的代码示例和说明。 ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • Webpack5内置处理图片资源的配置方法
    本文介绍了在Webpack5中处理图片资源的配置方法。在Webpack4中,我们需要使用file-loader和url-loader来处理图片资源,但是在Webpack5中,这两个Loader的功能已经被内置到Webpack中,我们只需要简单配置即可实现图片资源的处理。本文还介绍了一些常用的配置方法,如匹配不同类型的图片文件、设置输出路径等。通过本文的学习,读者可以快速掌握Webpack5处理图片资源的方法。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • Windows下配置PHP5.6的方法及注意事项
    本文介绍了在Windows系统下配置PHP5.6的步骤及注意事项,包括下载PHP5.6、解压并配置IIS、添加模块映射、测试等。同时提供了一些常见问题的解决方法,如下载缺失的msvcr110.dll文件等。通过本文的指导,读者可以轻松地在Windows系统下配置PHP5.6,并解决一些常见的配置问题。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文介绍了使用postman进行接口测试的方法,以测试用户管理模块为例。首先需要下载并安装postman,然后创建基本的请求并填写用户名密码进行登录测试。接下来可以进行用户查询和新增的测试。在新增时,可以进行异常测试,包括用户名超长和输入特殊字符的情况。通过测试发现后台没有对参数长度和特殊字符进行检查和过滤。 ... [详细]
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
author-avatar
手机用户2502939421
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有