Java+seleniumhq+phantomjsdriver+selenium-chrome-driver+selenium-firefox-driver截图

背景:项目需要自动报告生成,其中主要的复杂点及是报表的生成及截图写入doc文档,重点在报表的生成和截图

         于是引入了echarts(别人封装好的组件)+seleniumhq(自动化测试工具)

  • 引入POM依赖 

 <!-- https://mvnrepository.com/artifact/com.codeborne/phantomjsdriver -->
        <dependency>
            <groupId>com.codeborne</groupId>
            <artifactId>phantomjsdriver</artifactId>
            <version>1.4.4</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java -->
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>3.141.59</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-chrome-driver -->
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-chrome-driver</artifactId>
            <version>3.141.59</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-firefox-driver -->
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-firefox-driver</artifactId>
            <version>3.141.59</version>
        </dependency>
        <dependency>
            <groupId>com.github.abel533</groupId>
            <artifactId>ECharts</artifactId>
            <version>3.0.0.5</version>
            <exclusions>
                <exclusion>
                    <groupId>com.google.code.gson</groupId>
                    <artifactId>gson</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.6</version>
        </dependency>
  •  DriverConfig

package com.milla.report.config;

import com.milla.report.constant.ReportConstant;
import com.milla.report.enumeration.DriverEnum;
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.phantomjs.PhantomJSDriver;
import org.openqa.selenium.phantomjs.PhantomJSDriverService;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.logging.Level;

/**
 * @Package: com.aimsphm.nuclear.report.config
 * @Description: <浏览器驱动配置类>
 * @Author: MILLA
 * @CreateDate: 2020/4/26 13:53
 * @UpdateUser: MILLA
 * @UpdateDate: 2020/4/26 13:53
 * @UpdateRemark: <>
 * @Version: 1.0
 */
@Configuration
public class WebDriverConfig {
    @Bean("phantomJSDriver")
    public WebDriver phantomJSDriver() throws IOException {
//          //设置必要参数
        DesiredCapabilities options = new DesiredCapabilities();
        //ssl证书支持
        options.setCapability("acceptSslCerts", true);
        //截屏支持
        options.setCapability("takesScreenshot", true);
        //css搜索支持
        options.setCapability("cssSelectorsEnabled", true);
        //js支持
        options.setJavascriptEnabled(true);
        //驱动支持
        initPhantomJSEnvironment(options);
        //创建无界面浏览器对象
        PhantomJSDriver driver = new PhantomJSDriver(options);
//        driver.setLogLevel(Level.OFF);//关闭日志
        driver.manage().window().maximize();
        driver.setLogLevel(Level.ALL);
        return driver;
    }

    //    @Bean("chromeDriver")//目前linux执行存在问题
    public WebDriver chromeDriver() throws IOException {
        initLoadByOsName(DriverEnum.ChromeDriver);
        ChromeOptions options = new ChromeOptions();
        //设置 chrome 的无头模式
        options.setHeadless(Boolean.TRUE);
        //启动一个 chrome 实例
        return new ChromeDriver(options);
    }

    //    @Bean("firefoxDriver")//目前linux执行存在问题
    public WebDriver firefoxDriver() throws IOException {
        initLoadByOsName(DriverEnum.FirefoxDriver);
        FirefoxOptions options = new FirefoxOptions();
        //设置 chrome 的无头模式
        options.setHeadless(Boolean.TRUE);
        //启动一个 chrome 实例
        return new FirefoxDriver(options);
    }

    /**
     * 根据系统名称进行初始化
     *
     * @param driver
     * @throws IOException
     */
    private void initLoadByOsName(DriverEnum driver) throws IOException {
        ClassLoader classLoader = WebDriverConfig.class.getClassLoader();
        //获取操作系统的名字
        String osName = System.getProperty(ReportConstant.SYSTEM_CONSTANT_OS_NAME, ReportConstant.BLANK);
        if (osName.startsWith(ReportConstant.OS_NAME_PRE_MAC)) {//苹果的打开方式
            File file = decompressionDriver2TempPath(classLoader, driver.getDriverNameMac(), false);
            System.setProperty(driver.getBinPath(), file.getAbsolutePath());
        } else if (osName.startsWith(ReportConstant.OS_NAME_PRE_WINDOWS)) {//windows的打开方式
            File file = decompressionDriver2TempPath(classLoader, driver.getDriverNameWin(), false);
            System.setProperty(driver.getBinPath(), file.getAbsolutePath());
        } else {//unix,linux
            File file = decompressionDriver2TempPath(classLoader, driver.getDriverNameLinux(), true);
            System.setProperty(driver.getBinPath(), file.getAbsolutePath());


        }
    }

    /**
     * 解压文件到指定的工作路径
     *
     * @param classLoader 类加载器
     * @param driverName  需要运行的文件名称
     * @param isCmd       是否将文件设置程可执行文件
     * @return
     * @throws IOException
     */
    private File decompressionDriver2TempPath(ClassLoader classLoader, String driverName, boolean isCmd) throws IOException {
        //获取临时目录
        URL resource = classLoader.getResource(ReportConstant.PROJECT_DRIVER_ROOT_DIR + driverName);
        InputStream inputStream = resource.openStream();
        File file = new File(ReportConstant.SYSTEM_CONSTANT_OS_TEMP_DIR + File.separator + driverName);
        if (!file.exists()) {
            FileUtils.copyInputStreamToFile(inputStream, file);
            if (isCmd) {//将文件变成可执行状态
                Runtime r = Runtime.getRuntime();
                r.exec(ReportConstant.LINUX_EXECUTABLE_CMD_PRE + file.getAbsolutePath());
            }
        }
        return file;
    }

    /**
     * 初始化运行环境
     *
     * @param options 配置项
     * @throws IOException
     */
    private void initPhantomJSEnvironment(DesiredCapabilities options) throws IOException {
        ClassLoader classLoader = WebDriverConfig.class.getClassLoader();
        String phantomJSPath;
        //获取操作系统的名字
        String osName = System.getProperty(ReportConstant.SYSTEM_CONSTANT_OS_NAME, ReportConstant.BLANK);
        if (osName.startsWith(ReportConstant.OS_NAME_PRE_MAC)) {
            File file = decompressionDriver2TempPath(classLoader, DriverEnum.PhantomJSDriver.getDriverNameMac(), false);
            phantomJSPath = file.getAbsolutePath();
        } else if (osName.startsWith(ReportConstant.OS_NAME_PRE_WINDOWS)) {//windows的打开方式
            File file = decompressionDriver2TempPath(classLoader, DriverEnum.PhantomJSDriver.getDriverNameWin(), false);
            phantomJSPath = file.getAbsolutePath();
        } else {//unix,linux
            File file = decompressionDriver2TempPath(classLoader, DriverEnum.PhantomJSDriver.getDriverNameLinux(), true);
            phantomJSPath = file.getAbsolutePath();
        }
        options.setCapability(PhantomJSDriverService.PHANTOMJS_EXECUTABLE_PATH_PROPERTY, phantomJSPath);
    }
}

PS:Driver不用每次都生成,可以做成单例的,这样生成图片的速率会大大提升 ,如果是并发比较大的话可以做一个Driver的线程池来进行图片的生成(下次再实现吧)

  • 常量类 

package com.milla.report.constant;

import java.io.File;

/**
 * @Package: com.aimsphm.nuclear.report.constant
 * @Description: <常量类>
 * @Author: MILLA
 * @CreateDate: 2020/4/27 18:09
 * @UpdateUser: MILLA
 * @UpdateDate: 2020/4/27 18:09
 * @UpdateRemark: <>
 * @Version: 1.0
 */
public class ReportConstant {
    /**
     * Window系统前缀
     */
    public static final String OS_NAME_PRE_WINDOWS = "Windows";
    /**
     * Mac系统前缀
     */
    public static final String OS_NAME_PRE_MAC = "Mac OS";

    /**
     * Linux系统前缀
     */
    public static final String OS_NAME_PRE_LINUX = "Linux";
    /**
     * 变量中系统的key
     */
    public static final String SYSTEM_CONSTANT_OS_NAME = "os.name";
    /**
     * 驱动运行临时目录
     */
    public static final String SYSTEM_CONSTANT_OS_TEMP_DIR = File.separator + "usr" + File.separator + "share" + File.separator + "locale";
    /**
     * 驱动在项目中的根路径
     */
    public static final String PROJECT_DRIVER_ROOT_DIR = File.separator + "driver" + File.separator;
    /**
     * linux将文件变成可执行文件命令前缀
     */
    public static final String LINUX_EXECUTABLE_CMD_PRE = "chmod +x ";

    /**
     * 空字符串
     */
    public static final String BLANK = "";
    /**
     * 本地浏览器打开文件前缀
     */
    public static final String BROWSER_LOCAL_OPEN_PRE = "file:///";

    /**
     *echarts 图片tag名称
     */
    public static final String ECHARTS_CANVAS = "canvas";
}
  •  枚举类

 

package com.milla.report.enumeration;

/**
 * @Package: com.aimsphm.nuclear.report.enumeration
 * @Description: <驱动枚举类[可设置多版本]>
 * @Author: MILLA
 * @CreateDate: 2020/4/27 18:15
 * @UpdateUser: MILLA
 * @UpdateDate: 2020/4/27 18:15
 * @UpdateRemark: <>
 * @Version: 1.0
 */
public enum DriverEnum {
    ChromeDriver("webdriver.chrome.driver", "chromedriver-win32.exe", "chromedriver-mac", "chromedriver-linux64"),//谷歌驱动
    FirefoxDriver("webdriver.gecko.driver", "geckodriver-win64.exe", "geckodriver-mac", "geckodriver-linux64"),//火狐
    PhantomJSDriver("phantomjs.binary.path", "phantomjs-win.exe", "phantomjs-mac", "phantomjs-linux");

    DriverEnum(String binPath, String driverNameWin, String driverNameMac, String driverNameLinux) {
        this.binPath = binPath;
        this.driverNameWin = driverNameWin;
        this.driverNameMac = driverNameMac;
        this.driverNameLinux = driverNameLinux;
    }

    //驱动名称
    private String binPath;
    //window名称
    private String driverNameWin;
    //mac 名称
    private String driverNameMac;
    //linux名称
    private String driverNameLinux;

    public String getBinPath() {
        return binPath;
    }

    public String getDriverNameWin() {
        return driverNameWin;
    }

    public String getDriverNameMac() {
        return driverNameMac;
    }

    public String getDriverNameLinux() {
        return driverNameLinux;
    }
}

ps: 可以分不同的系统,分不同的浏览器版本,其中phantomjs是不需要浏览器支持的,可无浏览器操作截图

  • 截图工具类 

package com.milla.report.util;

import com.milla.report.constant.ReportConstant;
import org.openqa.selenium.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import java.util.Objects;

/**
 * @Package: com.aimsphm.nuclear.report.util
 * @Description: <截图工具类>
 * @Author: MILLA
 * @CreateDate: 2020/4/28 9:32
 * @UpdateUser: MILLA
 * @UpdateDate: 2020/4/28 9:32
 * @UpdateRemark: <>
 * @Version: 1.0
 */
@Component
public class ScreenshotUtils {
    @Autowired
    @Qualifier("phantomJSDriver")
    private WebDriver driver;

    /**
     * 直接通过driver截图 不延时
     *
     * @param htmlPath   html文件路径
     * @param outputType 输出类型
     * @param <X>        具体类型
     * @return
     * @throws WebDriverException
     */
    public <X> X getScreenshotAsByDriver(String htmlPath, OutputType<X> outputType) throws WebDriverException, InterruptedException {
        driver.get(ReportConstant.BROWSER_LOCAL_OPEN_PRE + htmlPath);
        return getScreenshotAs(htmlPath, outputType, null);
    }

    /**
     * 直接通过driver截图 延时
     *
     * @param htmlPath   html文件路径
     * @param outputType 输出类型
     * @param sleepTime  延时时间
     * @param <X>        具体类型
     * @return
     * @throws WebDriverException
     * @throws InterruptedException
     */
    public <X> X getScreenshotAsByDriver(String htmlPath, OutputType<X> outputType, Long sleepTime) throws WebDriverException, InterruptedException {
        //是否有html
        if (Objects.nonNull(htmlPath) && htmlPath.length() > 0) {
            driver.get(ReportConstant.BROWSER_LOCAL_OPEN_PRE + htmlPath);
        }
        //是否延时处理
        if (Objects.nonNull(sleepTime)) {
            Thread.sleep(sleepTime);
        }
        return ((TakesScreenshot) driver).getScreenshotAs(outputType);
    }

    /**
     * @param htmlPath   html文件路径
     * @param outputType 输出文件类型
     * @param sleepTime  延时时间
     * @param <X>        具体类型
     * @return
     * @throws WebDriverException
     * @throws InterruptedException
     */
    public <X> X getScreenshotAs(String htmlPath, OutputType<X> outputType, Long sleepTime) throws WebDriverException, InterruptedException {
        return getScreenshotAs(htmlPath, null, outputType, sleepTime);
    }

    /**
     * @param htmlPath   html文件路径
     * @param tagName    html中tag名称
     * @param outputType 输出类型
     * @param sleepTime  延时时间
     * @param <X>        具体类型
     * @return
     * @throws WebDriverException
     * @throws InterruptedException
     */
    public <X> X getScreenshotAs(String htmlPath, String tagName, OutputType<X> outputType, Long sleepTime) throws WebDriverException, InterruptedException {
        //是否有html
        if (Objects.nonNull(htmlPath) && htmlPath.length() > 0) {
            driver.get(ReportConstant.BROWSER_LOCAL_OPEN_PRE + htmlPath);
        }
        //是否延时处理
        if (Objects.nonNull(sleepTime)) {
            Thread.sleep(sleepTime);
        }
        //如果有tagName
        if (Objects.nonNull(tagName) && tagName.length() > 0) {
            return getScreenshotAs(outputType, tagName);
        }
        return getScreenshotAs(outputType, ReportConstant.ECHARTS_CANVAS);
    }

    /**
     * 默认tag不延迟
     *
     * @param htmlPath   html文件路径
     * @param outputType 输出类型
     * @param <X>        具体类型
     * @return
     * @throws WebDriverException
     * @throws InterruptedException
     */
    public <X> X getScreenshotAs(String htmlPath, OutputType<X> outputType) throws WebDriverException, InterruptedException {
        return getScreenshotAs(htmlPath, outputType, null);
    }

    /**
     * @param outputType 输出类型
     * @param tagName    html中元素名称
     * @param <X>        具体类型
     * @return
     * @throws WebDriverException
     */
    public <X> X getScreenshotAs(OutputType<X> outputType, String tagName) throws WebDriverException {
        //tag名称为空直接采用driver的截图方式
        if (Objects.isNull(tagName) && tagName.length() == 0) {
            return ((TakesScreenshot) driver).getScreenshotAs(outputType);
        }
        WebElement element;
        try {
            //获取不到指定tag的话使用driver进行截图
            element = driver.findElement(By.tagName(tagName));
        } catch (Exception e) {
            return ((TakesScreenshot) driver).getScreenshotAs(outputType);
        }
        WebDriver.Window window = driver.manage().window();
        Dimension size = element.getSize();
        window.setSize(new Dimension(size.width, size.getHeight()));
        return element.getScreenshotAs(outputType);
    }
}

 ps:在截图的时候需要对需要截图的tag进行大小设置,同时浏览器渲染的时候需要时间,这里设置了手动的延时时间,也可采用selenium中延时的API,这里就比较暴力的直接采用JavaApi实现了。

  •  生成Html文件

 public void scatter() throws IOException, InterruptedException {
        long currentTimeMillis = System.currentTimeMillis();
        GsonOption option = new GsonOption();
        //地址:http://echarts.baidu.com/doc/example/pie6.html
        option.tooltip(new Tooltip()
                .trigger(Trigger.axis)
                .showDelay(0)
                .axisPointer(new AxisPointer().type(PointerType.cross)
                        .lineStyle(new LineStyle()
                                .type(LineType.dashed).width(1))));
        option.legend("scatter1");
        option.toolbox().show(true).feature(Tool.mark, Tool.dataZoom, Tool.dataView, Tool.restore, Tool.saveAsImage);
        ValueAxis valueAxis = new ValueAxis().power(1).splitNumber(4).scale(true);
        option.xAxis(valueAxis);
        option.yAxis(valueAxis);
        //注:这里的结果是一种圆形一种方形,是因为默认不设置形状时,会循环形状数组
        option.series(
                new Scatter("scatter1").symbolSize("20").data(randomDataArray())
        );
        option.view();
        String exportToHtml = option.exportToHtml("/tmp/echarts/", "scatter2.html");
        //这里是测试,真实项目中需要得到文件流或二进制文件
        File srcFile = screenshot.getScreenshotAs(exportToHtml, OutputType.FILE, 300L);
        FileUtils.copyFile(srcFile, new File("/tmp/echarts/image" + System.currentTimeMillis() + ".png"));
        System.out.println("共计用时:" + (System.currentTimeMillis() - currentTimeMillis));
    }

 PS:此处是为测试,在项目中使用的是文件流的方式

  • 重要的模板 (template)

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Html2Image</title>
    <style>*{ margin:0} html,body{ height:50%} .wrapper{ min-height:100%; height:auto !important; height:100%; margin:0 auto -155px} .footer,.push{ height:155px} table.gridtable{ font-family:verdana,arial,sans-serif; font-size:11px; color:#333; border-width:1px; border-color:#666; border-collapse:collapse; margin:5px auto} table.gridtable th{ border-width:1px; padding:8px; border-style:solid; border-color:#666; background-color:#dedede} table.gridtable td{ border-width:1px; padding:8px; border-style:solid; border-color:#666; background-color:#fff} .middle{ text-align:center; margin:0 auto; width:90%; height:auto} .info{ font-size:12px; text-align:center; line-height:20px; padding:40px} .info a{ margin:0 10px; text-decoration:none; color:green}</style>
</head>
<body >
<div class="wrapper">
    <div class="middle">
       <!-- <h1 style="padding: 70px 0 20px;">ECharts效果</h1> -->
        <!-- 为ECharts准备一个具备大小(宽高)的Dom -->
        <div id="main" style="height:400px"></div>
    </div>
</div>
</body>
<!-- ECharts单文件引入 -->
<script src="http://echarts.baidu.com/gallery/vendors/echarts/echarts-all-3.js"></script>
<script type="text/javascript">
// 基于准备好的dom,初始化echarts图表
var myChart = echarts.init(document.getElementById('main'));

var option = ##option##;

// 为echarts对象加载数据
myChart.setOption(option);
</script>
</html>

 PS:echarts组件中的模板是采用OptionUtil加载文件的方式,插件已经停止更新,所以模板的名称是固定的template(或改其源码也可以)

下面就可以愉快的玩耍了,哈哈

划重点了:
    1、为了防止跨平台需要额外的运维,可将可执行文件都放在Project中,产生的问题是,打包之后的Project中是没有办法将可执行文件放在系统变量汇总的,所以需要通过JavaAPI方式将可执行文件导入到服务器的工作目录,并将其变成可执行文件,方可执行

    2、Java8以后可以通过JavaAPI方式执行Js文件

ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName( "JavaScript" );
 
System.out.println( engine.getClass().getName() );
System.out.println( "Result:" + engine.eval( "function f() { return 1; }; f() + 1;" ) );

   3、在linux中执行的时候,截图出来的图片始终是乱码的,大部分因为字体不支持的原因

//在/usr/share/fonts 目录下创建micro文件夹

[root@elastic-slave ~]# mkdir /usr/share/fonts/micro

//安装依赖
[root@elastic-slave ~]# yum install bitmap-fonts bitmap-fonts-cjk

//然后将windows中的fonts文件夹直接全部导过去,重新运行程序即可
 

 4、使用chrom和firefox在window上时,并无任何问题,但是在linux上还是不能执行,原因猜测:linux安装的浏览器和浏览器驱动版本不一致

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值