美文网首页
java使用phantomjs进行截图

java使用phantomjs进行截图

作者: 何甜甜在吗 | 来源:发表于2018-10-08 16:19 被阅读0次

    断断续续查找资料、验证不同的实现方法终于算基本搞定了页面截图,因为中间过程曲折花费较多时间,分享出来帮助大家快速实现截图

    为什么选用phantomjs进行截图

    截图可以实现的方式有很多,比如:

    • selenium
    • HtmlUnit
    • Html2Image
      、、、and so on
      但是这些实现的截图效果都不好。selenium只能实现截屏,不能截取整个页面,而HtmlUnit、Html2Image对js的支持效果并不好,截下来的图会有很多空白。phantomjs就是万精油了,既能截取整个页面,对js支持的效果又好

    前期准备

    安装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
    
    • 查看效果


      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大小,但是不,我不能。

    难受.jpeg

    并且不能接受预先设定一个很大的height值,比如30000,因为不能接受底下留白的效果
    尝试二:
    在window.setTimeout方法之前加入以下代码

     page.evaluate(function(){
         scrollBy(0, 18000); 
    });
    

    无奈evaluate里不能在用for循环了,前端渣渣真的不知道如何改,遂放弃

    java代码方式进行截图

    • 需要的依赖
           <dependency>
                <groupId>org.seleniumhq.selenium</groupId>
                <artifactId>selenium-java</artifactId>
                <version>2.45.0</version>
            </dependency>
    
            <dependency>
                <groupId>com.codeborne</groupId>
                <artifactId>phantomjsdriver</artifactId>
                <version>1.2.1</version>
                <!-- this will _always_ be behind -->
                <exclusions>
                    <exclusion>
                        <groupId>org.seleniumhq.selenium</groupId>
                        <artifactId>selenium-java</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.seleniumhq.selenium</groupId>
                        <artifactId>selenium-remote-driver</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
    
    
    • 代码实现
    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来不及加载
    该方式的缺点:比较费时间。果然熊和鱼掌不可兼得也,统计了一下截取一张图片大概需要四分多钟
    ===================更新于2018.10.15====================

    phantomjs的缺点

    • 有最大截图长度32767px
    • 可能会出现跨越的问题
    • 需要装浏览器驱动

    可以使用google的puppeteer完成截图功能:
    附简单demo:

    const puppeteer = require('/usr/local/lib/node_modules/puppeteer');
    
    (async () => {
        const browser = await puppeteer.launch({
            headless: false
        });
        const page = await browser.newPage();
        // await page.goto('https://www.zhihu.com/question/22263777');
        await page.goto('http://www.iqiyi.com');
        await page.setViewport({
            width: 1200,
            height: 800
        });
    
        await autoScroll(page);
    
        await page.screenshot({
            path: 'jd.png',
            fullPage: true
        });
    
        await browser.close();
    })();
    
    
    function autoScroll(page) {
        return page.evaluate(() => {
            return new Promise((resolve, reject) => {
                var totalHeight = 0;
                var distance = 100;
                var timer = setInterval(() => {
                    var scrollHeight = document.body.scrollHeight;
                    window.scrollBy(0, distance);
                    totalHeight += distance;
    
                    if (totalHeight >= scrollHeight) {
                        clearInterval(timer);
                        resolve();
                    }
                }, 100);
            })
        });
    }
    

    相关文章

      网友评论

          本文标题:java使用phantomjs进行截图

          本文链接:https://www.haomeiwen.com/subject/jbcjaftx.html