前言
最近工作中遇到一个需求,需要实现截图功能,断断续续查找资料、验证不同的实现方法终于算基本搞定了页面截图,因为中间过程曲折花费较多时间,分享出来帮助大家快速实现截图
为什么选用phantomjs进行截图
截图可以实现的方式有很多,比如:
- selenium
- HtmlUnit
- Html2Image、、、and so on但是这些实现的截图效果都不好。selenium只能实现截屏,不能截取整个页面,而HtmlUnit、Html2Image对js的支持效果并不好,截下来的图会有很多空白。phantomjs就是万精油了,既能截取整个页面,对js支持的效果又好
PlantomJs提供了如 CSS 选择器、DOM操作、JSON、HTML5、Canvas、SVG 等。PhantomJS 的用处很广泛,如网络监控、网页截屏、页面访问自动化、无需浏览器的 Web 测试等,这里只用到网页截屏。
前期准备
安装phantomjs。mac os
brew install phantomjs
命令行的方式进行截图
安装以后我们就可以小试牛刀了
打开终端,输入以下命令:
/Users/hetiantian/SoftWares/phantomjs/bin/phantomjs /Users/hetiantian/SoftWares/phantomjs/examples/rasterize.js https://juejin.im/post/5bb24bafe51d450e4437fd96 /Users/hetiantian/Desktop/juejin-command.png
查看效果
发现图片没有加载好
来看以下刚刚的命令行:
/Users/hetiantian/SoftWares/phantomjs/bin/phantomjs:phantomjs可执行文件保存地址
/Users/hetiantian/SoftWares/phantomjs/examples/rasterize.js:rasterize.js文件地址
这段命令可以理解为用phantomjs去运行rasterize.js文件,所以要想解决图片空白的问题我们需要去看一下rasterize.js文件。
"use strict"; var page = require('webpage').create(), system = require('system'), address, output, size, pageWidth, pageHeight; if (system.args.length < 3 || system.args.length > 5) { console.log('Usage: rasterize.js URL filename [paperwidth*paperheight|paperformat] [zoom]'); console.log(' paper (pdf output) examples: "5in*7.5in", "10cm*20cm", "A4", "Letter"'); console.log(' image (png/jpg output) examples: "1920px" entire page, window width 1920px'); console.log(' "800px*600px" window, clipped to 800x600'); phantom.exit(1); } else { address = system.args[1]; output = system.args[2]; page.viewportSize = { width: 600, height: 600 }; if (system.args.length > 3 && system.args[2].substr(-4) === ".pdf") { size = system.args[3].split('*'); page.paperSize = size.length === 2 ? { width: size[0], height: size[1], margin: '0px' } : { format: system.args[3], orientation: 'portrait', margin: '1cm' }; } else if (system.args.length > 3 && system.args[3].substr(-2) === "px") { size = system.args[3].split('*'); if (size.length === 2) { pageWidth = parseInt(size[0], 10); pageHeight = parseInt(size[1], 10); page.viewportSize = { width: pageWidth, height: pageHeight }; page.clipRect = { top: 0, left: 0, width: pageWidth, height: pageHeight }; } else { console.log("size:", system.args[3]); pageWidth = parseInt(system.args[3], 10); pageHeight = parseInt(pageWidth * 3/4, 10); // it's as good an assumption as any console.log ("pageHeight:",pageHeight); page.viewportSize = { width: pageWidth, height: pageHeight }; } } if (system.args.length > 4) { page.zoomFactor = system.args[4]; } page.open(address, function (status) { if (status !== 'success') { console.log('Unable to load the address!'); phantom.exit(1); } else { window.setTimeout(function () { page.render(output); phantom.exit(); }, 200); } }); }
尝试一:
对page.viewportSize = { width: 600, height: 600 };产生了疑问️
把height调大十倍,发现基本是完美截图了,但是如果页面的篇幅特别短,会发现有瑕疵,下面留有一大片空白。原因:page.viewportSize = { width: 600, height: 600 };设置的是初始打开浏览器的大小,通过增大这个值可以加载js。如果我们能拿到实际页面的大小在设置height大小,但是不,我不能。
并且不能接受预先设定一个很大的height值,比如30000,因为不能接受底下留白的效果
尝试二:
在window.setTimeout方法之前加入以下代码
page.evaluate(function(){ scrollBy(0, 18000); });
无奈evaluate里不能在用for循环了,前端渣渣真的不知道如何改,遂放弃
java代码方式进行截图
需要的依赖
org.seleniumhq.selenium selenium-java 2.45.0 com.codeborne phantomjsdriver 1.2.1 org.seleniumhq.selenium selenium-java org.seleniumhq.selenium selenium-remote-driver
代码实现
public class PhantomjsTest2 { public static void main(String[] args) throws InterruptedException, IOException { //设置必要参数 DesiredCapabilities dcaps = new DesiredCapabilities(); //ssl证书支持 dcaps.setCapability("acceptSslCerts", true); //截屏支持 dcaps.setCapability("takesScreenshot", true); //css搜索支持 dcaps.setCapability("cssSelectorsEnabled", true); //js支持 dcaps.setJavascriptEnabled(true); //驱动支持(第二参数表明的是你的phantomjs引擎所在的路径) dcaps.setCapability(PhantomJSDriverService.PHANTOMJS_EXECUTABLE_PATH_PROPERTY, "/Users/hetiantian/SoftWares/phantomjs/bin/phantomjs"); //创建无界面浏览器对象 PhantomJSDriver driver = new PhantomJSDriver(dcaps); //设置隐性等待(作用于全局) driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS); long start = System.currentTimeMillis(); //打开页面 driver.get("https://juejin.im/post/5bb24bafe51d450e4437fd96"); Thread.sleep(30 * 1000); JavascriptExecutor js = driver; for (int i = 0; i < 33; i++) { js.executeScript("window.scrollBy(0,1000)"); //睡眠10s等js加载完成 Thread.sleep(5 * 1000); } //指定了OutputType.FILE做为参数传递给getScreenshotAs()方法,其含义是将截取的屏幕以文件形式返回。 File srcFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE); Thread.sleep(3000); //利用FileUtils工具类的copyFile()方法保存getScreenshotAs()返回的文件对象 FileUtils.copyFile(srcFile, new File("/Users/hetiantian/Desktop/juejin-01.png")); System.out.println("耗时:" + (System.currentTimeMillis() - start) + " 毫秒"); } }
注释已经够详细了不多说了。唯一说一点:通过去执行js代码实现页面滑动,并且每次滑动都会通过睡眠保证有时间可以将 js加载进来。会调用33次滑动,因为phantomjs截取最大的高度为32767px(int 32位的最大整数),所以滑动33次可以保证能够截取到的最大页面部分其js已经是加载完成了的
附:window.scrollBy(0,1000)、window.scrollTo(0,1000)的区别
window.scrollBy(0,1000)
window.scrollBy(0,1000)
执行到这里页面滑动1000+1000px
window.scrollTo(0,1000)
window.scrollTo(0,1000)
执行到这里页面滑动到1000px处
window.scrollTo(0, document.body.scrollHeight可以滑动到页面底部,不选择有两个原因:
1)一下子滑动到底部js会来不及被加载
2)有些页面没有底部,可以一直滑动加载
注:这里所说的js来不及加载指的是:想要截取页面的js来不及加载
该方式的缺点:比较费时间。果然熊和鱼掌不可兼得也,统计了一下截取一张图片大概需要四分多钟
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。