PhantomJS介绍

PhantomJS是一个基于webkit内核的无头浏览器,即没有UI界面的一个浏览器,只是其内的点击、翻页等人为相关操作需要程序设计实现。PhantomJS提供JavaScript API接口,即通过编写js程序可以直接与webkit内核交互,在此之上可以结合Java语言等,通过java调用js等相关操作,从而解决了以前c/c++才能比较好的基于webkit开发优质采集器的限制。

PhantomJS的安装配置

windows环境

如果是在windows环境下,则在官网下载解压到某个目录后,将其bin目录加入到path变量中即可。

Linux环境

如果是在Linux环境下,在官网下载解压后,同样需要将PhantomJSbin目录加入到path环境变量中,参考的命令和配置如下:

# 编辑配置文件.
vi ~/.bashrc

# 将PhantomJS的bin目录加入到PATH环境变量中.
export PHANTOMJS_HOME=/home/blinkfox/Documents/phantomjs-2.1.1-linux-x86_64
export PATH=${PHANTOMJS_HOME}/bin:$PATH

# 退出vi编辑器,使用source命令让刚才的配置即时生效.
source ~/.bashrc

# 测试PhantomJS是否安装成功,如果打出了版本信息,即安装成功.
phantomjs -v

demo示例

这个demo的需求是这样的,我们使用Java调用PhantomJS的指令来在服务端加载含ECharts统计的图jsp文件,等待jsp页面渲染成功之后,利用PhantomJS浏览器的截图功能,截图后保存到指定目录

首先,制作ECharts的jsp页面,示例页面如下代码如下:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>测试的ECharts数据统计图</title>
</head>
<body>
    <!-- 为 ECharts 准备一个具备大小(宽高)的 DOM -->
    <div id="main" style="width:560px; height:270px;"></div>

<script type="text/javascript" src="/js/lib/jquery/jquery-1.9.1.min.js"></script>
<script type="text/javascript" src="/js/lib/echarts/v3/echarts.min.js"></script>
<script type="text/javascript">

// 基于准备好的dom,初始化echarts实例
var myChart = echarts.init(document.getElementById('main'));

// 指定图表的配置项和数据
var option = {
    title: {
        text: 'ECharts 入门示例'
    },
    animation: false, // 关闭动画效果
    tooltip: {},
    legend: {
        data:['销量']
    },
    xAxis: {
        data: ["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"]
    },
    yAxis: {},
    series: [{
        name: '销量',
        type: 'bar',
        data: [5, 20, 36, 10, 10, 20]
    }]
};

// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);

</script>
</body>
</html>

然后,是书写PhantomJS脚本echarts_load.js来加载和调用图片下载的代码:

ps:phantomjs 基础及实例可看此文章:

var system = require('system');
var page = require('webpage').create();

// 如果是windows,设置编码为gbk,防止中文乱码,Linux本身是UTF-8
var osName = system.os.name;
console.log('os name:' + osName);
if ('windows' === osName.toLowerCase()) {
    phantom.outputEncoding="gbk";
}

// 获取第二个参数(即请求地址url).
var url = system.args[1];
console.log('url:' + url);

// 显示控制台日志.
page.onConsoleMessage = function(msg, lineNum, sourceId) {
    console.log('CONSOLE: ' + msg + ' (from line #' + lineNum + ' in "' + sourceId + '")');
};

//打开给定url的页面.
var start = new Date().getTime();
page.open(url, function(status) {
    if (status == 'success') {
        console.log('echarts页面加载完成,加载耗时:' + (new Date().getTime() - start) + ' ms');

        // 由于echarts动画效果,延迟500毫秒确保图片渲染完毕再调用下载图片方法.
        setTimeout(function() {
            page.evaluate(function() {
                //name带上相对路径即可 比如“/aaa/bbb/aaa.png”
                page.render(name);
                console.log("调用了echarts的截图图片功能.");
            });
        }, 500);
    } else {
        console.log("页面加载失败 Page failed to load!");
    }

    // 3秒后再关闭浏览器.
    setTimeout(function() {
        phantom.exit();
    }, 3000);
});

最后,是使用Java来调用PhantomJS的指令,代码如下:

public class HttpTest {

    private static final Logger log = LoggerFactory.getLogger(HttpTest.class);

    private static final String PHANTOM_PATH = "phantomjs";

    //这里我的test.js是保存在G盘下面的phantomjs目录
    private static final String TEST_JS = "G:/test/phantom/test.js ";

    public void String downloadImage(String url) throws IOException {
        String cmdStr = PHANTOM_PATH + TEST_JS + url;
        log.info("命令行字符串:{}", cmdStr);

        Runtime rt = Runtime.getRuntime();
        try {
            rt.exec(cmdStr);
        } catch (IOException e) {
            log.error("执行phantomjs的指令失败!请检查是否安装有PhantomJs的环境或配置path路径!PhantomJs详情参考这里:http://phantomjs.org", e);
        }
    }

    /**
     * main.
     * @param args args
     * @throws IOException IO异常
     */
    public static void main(String[] args) throws IOException {
        downloadImage("http://127.0.0.1:8080/test/echart_test/test_echarts.html");
    }

    /**
     * @Title: MakeHtml 
     * @Description: 创建html
     * @param    head html 标题
     * @param    body 需要显示的内容
     * @param    disrPath  生成html的存放路径
     * @return void    返回类型 
     * @throws
     */
    public static void MakeHtml(String head,String body,String disrPath ){
        try {
        	String templateContent="";
            FileInputStream fileinputstream = new FileInputStream(REPORT_TEMPLATE);// 读取模板文件
            log.info("模板路径:"+REPORT_TEMPLATE);
            int lenght = fileinputstream.available();
            byte bytes[] = new byte[lenght];
            fileinputstream.read(bytes);
            fileinputstream.close();
            templateContent = new String(bytes);
            templateContent = templateContent.replaceAll("###head###", head);
            log.info("标题:"+head);
            templateContent = templateContent.replaceAll("###body###", body);
            log.info("图片:"+body);
            log.info(templateContent);
            
            String filename = PATH+disrPath;// 生成的html文件保存路径。
            FileOutputStream fileoutputstream = new FileOutputStream(filename);// 建立文件输出流
            log.info("文件输出路径:"+filename);
            
            byte tag_bytes[] = templateContent.getBytes();
            fileoutputstream.write(tag_bytes);
            fileoutputstream.close();
        } catch (Exception e) {
            e.printStackTrace();
            log.error("makeHtml error:"+e.getMessage(),e);
        }
    }
    
    /**
     * 文件夹打包
     * @param srcDir 需打包的文件 f:/abc
     * @param outFile 输出路径 f:/abc.zip
     * 不会自动生成文件夹需自行判断
     */
    public static void toZip(String srcDir, String outFile) {
    	FileOutputStream out;
    	//判断存储路径是否存在(只有一级)
    	String filePath=outFile.substring(0,outFile.lastIndexOf("/"));
    	File file=new File(PATH+filePath);
    	if(!file.exists()) {
    		file.mkdir();
    	}
    	
		try {
			Thread.sleep(30000);
			out = new FileOutputStream(new File(PATH+outFile));
			ZipUtils.toZip(PATH+srcDir, out,true);
		} catch (Exception e) {
			log.error("客户月报截图打包打包失败:"+e.getMessage(),e);
			log.error(e.getMessage(),e);
		}
    }

}