18 Star 132 Fork 61

木子灬聖/EChartsConvert

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README

PhantomJS在服务端生成ECharts图片

主要功能:给定ECharts的option参数,生成ECharts图片,并以Base64字符串返回。

1. PhantomJS 介绍

PhantomJS是一个不需要浏览器的富客户端。

官方介绍:PhantomJS是一个基于 WebKit 的服务器端JavaScript API。它全面支持web而不需浏览器支持,支持各种Web标准:DOM处理,CSS选择器, JSON,Canvas,和SVG。
PhantomJS常用于页面自动化,网络监测,网页截屏,以及无界面测试等。

通常我们使用PhantomJS作为爬虫工具。传统的爬虫只能单纯地爬取html的代码,对于js渲染的页面,就无法爬取,如Echarts统计图。而PhantomJS正可以解决此类问题。

我们可以这么理解PhantomJS,PhantomJS是一个无界面、可运行脚本的谷歌浏览器。

1.1 PhantomJS下载安装

PhantomJS安装非常简单,官网http://phantomjs.org/download.html下载最新的安装包, 安装包有Windows,Mac OS X, Linux 64/32 bit,选择对应的版本下载解压即可使用,在下载包里有个example文件夹,里面对应了许多示例供参考。

为方便使用,我们将phantomjs添加至环境变量中。

windows:
右键我的电脑->属性->高级系统设置->高级->环境变量->用户变量/系统变量->Path=D:\phantomjs\bin;
或
cmd->set path=%path%;D:\phantomjs\bin

linux:
vi /etc/profile
export PATH=$PATH:/usr/phantomjs/bin

注:linux虽然不需要其他的依赖包,但仍旧需要GLIBCXX_3.4.9和GLIBC_2.7,当然大多数linux是有这两个依赖包的。

1.2 PhantomJS运行脚本

进入example文件夹,里面有个hello.js脚本:

"use strict";
console.log('Hello, world!');
phantom.exit();

通过phantomjs hello.js即可运行脚本hello.js

phantomjs hello.js

控制台输出:Hello, world!

1.3 PhantomJS脚本参数

example文件夹中有arguments.js脚本:

"use strict";
var system = require('system');
if (system.args.length === 1) {
    console.log('Try to pass some args when invoking this script!');
} else {
    system.args.forEach(function (arg, i) {
        console.log(i + ': ' + arg);
    });
}
phantom.exit();

运行以下命令,其中(first second third)是参数:

phantomjs arguments.js first second third

控制台将输出:

0: arguments.js
1: first
2: second
3: third

1.4 PhantomJS页面加载

创建pageload.js脚本:

"use strict";
var page = require('webpage').create();
page.open('https://www.baidu.com/', function () {
    page.render('baidu.png');
    phantom.exit();
});

运行phantomjs pageload.js即可在同级目录下得到一张baidu.png的图片

由于它的这个特性,PhantomJS 可以用来“网页截屏”,截取网页的快照,比如将网页、SVG存成图片,PDF等

导出ECharts图片也是基于page.render此功能

2. PhantomJS API

PhantomJS除了有examples外,还有比较全面的API,官方地址http://phantomjs.org/api/

主要包括:

  1. Command Line Interface(命令行):PhantomJS内置的一些命令行选项,比如设置编码--output-encoding=encoding 、设置代理--proxy=address等等;
  2. phantom Object(phantom对象):phantom为实现各种接口,增加一个宿主对象(host object),同时也是window的子对象。主要功能包括运行脚本路径phantom.libraryPath,phantom退出phantom.exit(returnValue)等等
  3. Web Page Module(网页模块):核心模块,该模块主要提供页面自动化,网页截屏等等功能。
  4. Child Process Module(子进程模块):PhantomJS调用子进程并通过stdin,stdout,stderr进行通信。(to learn)
  5. File System Module(文件系统模块):读取和操作服务器系统目录和文件。
  6. System Module(系统模块):该模块可以获取命令行参数、环境变量、操作系统和PID信息。
  7. Web Server Module(Web服务器模块):内置web服务器提供http服务。

下面将介绍部分在echarts-convert.js中使用到的API,便于方便查看脚本

2.1 phantom Object(phantom对象)

phantom.libraryPath:获取运行脚本路径,如该脚本的路径为"E:/phantomjs/examples/hello.js",那么phantom.libraryPath=E:/phantomjs/examples,注意最后没有"/"。

phantom.exit():结束phantom进程,脚本不写phantom.exit()时,phantom将不会自己退出,因此如非特殊场景必须增加退出功能。

phantom.onError = function (msg, trace) {}:全局异常监听,如果page.onError或其他异常没有捕获,该事件将捕获错误信息。

2.2 Web Page Module(网页模块)

首先必须引入此模块var page = require('webpage').create();

page.open(url,callback):打开一个url并且加载页面,页面加载完毕后,执行回调函数callback。简单来说就是在phantom浏览器里中打开一个页面。此方法有几个重载函数,可以去官网查看。

page.close():关闭页面,主要为了释放内存。

page.evaluate(function, arg1, arg2, ...):评估即沙盒测试,在当前页面建立一个沙盒,在沙盒上做一些操作。该功能比较强大,可以用于无界面测试。

page.injectJs(filename):将外部JavaScript文件注入到页面中。主要用于沙盒测试。

page.render(filename [, {format, quality}]):渲染也就是截图,生成图片或PDF,filename为路径,format包括PDF、PNG、JPEG、BMP等,quality图片质量为0-100。

page.renderBase64(format):渲染生成图片的Base64字符串,format包括PNG、JPEG、GIF。

page.clipRect = {top: 0,left: 0,width: 400,height: 300}:截图范围,如果没有设置将截取全部页面。注:phantomjs默认的width为400

page.onConsoleMessage = function (msg, lineNum, sourceId){}:页面的控制台监听,用于测试时监听控制台信息。

page.onError = function (msg, trace) {}:页面错误监听,如果没有特殊需求,该监听一定要写,否则页面代码错误将无法捕获。

2.3 File System Module(文件系统模块)

首先引入var fs = require('fs');模块

fs.workingDirectory:脚本运行目录,与phantom.libraryPath一样。

fs.exists(string):文件是否存在。

fs.makeDirectory(string):创建一个目录。

2.4 System Module(系统模块)

首先必须引入var system = require('system');模块

system.args:返回命令行参数数组,第一个参数是脚本名,其他的是各个参数。

system.pid:正在运行的进程id。

2.5 Web Server Module(Web服务器模块)

首先引入var server = require('webserver').create();模块

server.listen(port, function (request, response) {}:开启监听服务。

request 参数
request.method:请求方式'GET'或'POST'等等
request.url:请求地址,包括GET的参数
request.post:请求体,仅在'POST'和'PUT'时存在

response 参数
response.statusCode:http状态码
response.headers:http头部信息
response.write(data):响应体写入数据
response.close():关闭http连接

2.6 自定义Module

examples目录中,有module.jsuniverse.js两个脚本:

module.js

"use strict";
var universe = require('./universe');
universe.start();
console.log('The answer is ' + universe.answer);
phantom.exit();

universe.js

"use strict";
exports.answer = 42;

exports.start = function () {
    console.log('Starting the universe....');
}

自定义module跟nodeJs类似,主要是module.js通过require('./universe')加载universe这个模块,然后就可以直接访问universe.js中exports对象的成员函数。

3. 设计思路

3.1 初步思路

1. Web项目提供一个http服务,并公开此url地址,访问该地址可以获取Echarts统计图;
2. Java通过Runtime调用phantomjs脚本,并传入url和filename参数,相当于打开浏览器;
3. 使用page.open(url)打开该地址,相当于在浏览器中浏览访问;
4. 使用page.render(filename)生成图片,相当于截图保存;
5. Java读取filename图片文件File,将其转换成Base64字符串即可。

此思路的流程是按照人的操作流程实现的,phantomjs在这里就是一个浏览器。首先有地址可访问,打开phantomjs,访问地址,截图保存,最后获取图片。

这种设计需要在Web项目开发一个或多个页面,供phantomjs调用。首先增加开发工作量,并导致项目存在无关的页面,最后耦合度较高,不利于维护。

3.2 中期思路

web项目不开发页面,利用Web Page Module模块中的沙盒,生成所需的页面。
查看ECharts的文档,统计图的创建主要是通过option(配置项和数据)来控制,因此可以通过option动态生成不同的统计图。

1. Java通过Runtime调用phantomjs脚本,传入ECharts的option和filename参数;
2. 使用page.open(about:blank)打开空页面;
3. 调用page.evaluate()在空页面上创建ECharts的Dom层和JavaScript代码;
4. 使用page.render(filename)生成图片;
5. Java读取filename图片文件File,将其转换成Base64字符串即可。

这种方式可以解耦合,我们只需关注ECharts的option怎么传入。但是通过Java的Runtime多次打开phantomjs进程,不仅效率慢,而且大量处理时吃服务器资源。

3.3 改进思路(目前实现)

为解决多次开启进程带来的资源消耗提高性能,主要是减少phantomjs进程的多次打开与关闭,目前的解决思路有两种:
一、利用Child Process Module(子进程模块),开启phantomjs进程时,同时启动Java进程作为子进程。通过共享的stdin,stdout,stderr进行通信。
二、利用Web Server Module(Web服务器模块),开启web服务,java通过http请求发送数据并获取结果。

1. 使用server.listen()开启Web服务。
2. java通过url请求地址,并传入ECharts的option参数;
3. 使用page.open(about:blank)打开空页面;
4. 调用page.evaluate()在空页面上创建ECharts的Dom层和JavaScript代码;
5. 使用page.renderBase64(format)生成图片的Base64字符串;
6. response返回Base64数据。

3.4 后续版本

在实际使用过程中会出现各种问题,待改进。

4. 代码实现

4.1 引入module

// 引入module
var system = require('system'), // 获取参数
    path = phantom.libraryPath,
    command = require(path + '/module/command.js');// 参数module

其中system为了获取参数选项,path是当前脚本路径,command是命令行解析模块。

4.2 设置选项并解析参数

var commandParams = command
    .version('0.0.1')
    .option('-s, --server', 'provide echarts convert http server')
    .option('-p, --port <number>', 'change server port when add -s or --server', 9090)
    .option('-o, --opt <json>', 'add the param of echarts method [ eChart.setOption(opt) ]')
    .option('-t, --type <value>', 'provide file/base64 for image, default file', /^(file|base64)$/i, 'base64')
    .option('-f, --outfile <path>', 'add output of the image file path')
    .option('-w, --width <number>', 'change image width', '800')
    .option('-h, --height <number>', 'change image height', '400')
    .parse(system.args);

以port为例,-p是短选项,--port是长选项,是命令后必须跟参数,'change server port when add -s or --server'是描述信息,9090是默认值。

参数介绍:

-s或--server:开启服务监听,如果开启服务端,则-o不生效,这也就意味不能使用脚本命令生成图片;
-p或--port:端口号,只有-s启用时生效,改变监听端口号;
-o或--opt:ECharts的option,这里是json字符串,最外层不加引号;
    示例:{title:{text:'ECharts示例'},tooltip:{},legend:{data:['销量']},xAxis:{data:['衬衫','羊毛衫','雪纺衫','裤子','高跟鞋','袜子']},yAxis:{},series:[{name:'销量',type:'bar',data:[5,20,36,10,10,20]}]}
-t或--type:生成类型file或base64,file是生成图片,base64是生成Base64字符串;
-f或--outfile:文件输出路径,只有--type=file时生效,如果不指定,则在脚本目录下创建一个tmp文件夹,将图片以时间戳为名字,保存到tmp目录下;
-w或--width:生成的图片宽度,默认800像素;
-h或--height:生成的图片高度,默认400像素。

服务端参数

GET或POST请求时,request的参数主要包括:

opt:与--opt等同,ECharts的option,这里是json字符串;
type:与--type等同,生成类型file或base64;
width:与--width等同,生成的图片宽度,默认800像素;
height:与--height等同,生成的图片高度,默认400像素。

system.args获取命令行参数,parse(system.args)解析参数,之后可以通过commandParams.server,commandParams.port获取相关值。

4.3 创建对象并初始化

// ***********************************
// Echarts转换器
// ***********************************
function Convert(params) {
    this.params = params || commandParams; // 参数命令
    this.external = {
        JQUERY3: path + '/script/jquery-3.2.1.min.js',
        ECHARTS3: path + '/script/echarts.min.js',
        ECHARTS_CHINA: path + '/script/china.js'
    }; // 外部js
}

/**
 * 初始化
 */
Convert.prototype.init = function () {
    var params = this.params;
    this.check(params);
    if (params.server) {
        this.server(params);
    } else {
        this.client(params);
    }
};

创建Convert对象,对象会保存命令行参数选项以及常量如外部依赖js的路径。

init()主要是检查参数,并判断开启服务端还是客户端处理,参数检查详见Convert.prototype.check

4.4 开启服务监听

/**
 * 服务
 * @param params
 */
Convert.prototype.server = function (params) {
    var server = require('webserver').create(), // 服务端
        convert = this;

    var listen = server.listen(params.port, function (request, response) {
        /**
         * 输出
         * @param data
         * @param success
         */
        function write(data, success, msg) {
            response.statusCode = 200;
            response.headers = {
                'Cache': 'no-cache',
                'Content-Type': 'application/json;charset=utf-8'
            };
            response.write(convert.serverResult(data, success, msg));
            response.close();
        }

        //获取参数
        var args = convert.serverGetArgs(request);

        if (args.opt !== undefined) {
            var check = convert.serverCheckAndSet(params, args);

            if (check) {
                convert.client(params, write);
            } else {
                write("", false, "get image error,please check parameter [opt]");
            }
        } else {
            write("", false, "missing parameter [opt]");
        }

    });

    // 判断服务是否启动成功
    if (!listen) {
        this.error("could not create echarts-convert server listening on port " + params.port);
    } else {
        console.log("echarts-convert server start success. [pid]=" + system.pid);
    }
};

var listen = server.listen(params.port, function (request, response) {}开启服务,返回值listen如果为false时则启动失败。

Convert.prototype.serverGetArgs获取并解析request参数,这里只处理了GET和POST请求,解析参数时一定要进行解码处理,尤其是中文。

/**
 * 获取参数
 * @param request
 * @returns {{}}
 */
Convert.prototype.serverGetArgs = function (request) {
    var args = {};
    if ('GET' === request.method) {
        var index = request.url.indexOf('?');
        if (index !== -1) {
            var getQuery = request.url.substr(index + 1);
            args = this.serverParseArgs(getQuery);
        }
    } else if ('POST' === request.method) {
        var postQuery = request.post;
        args = this.serverParseArgs(postQuery);
    }
    return args;
};

/**
 * 解析参数
 * @param query 字符串
 * @returns {{}} 对象
 */
Convert.prototype.serverParseArgs = function (query) {
    var args = {},
        pairs = query.split("&");
    for (var i = 0; i < pairs.length; i++) {
        var pos = pairs[i].indexOf('=');
        if (pos === -1)
            continue;
        var key = pairs[i].substring(0, pos);
        var value = pairs[i].substring(pos + 1);
        // 中文解码,必须写两层
        value = decodeURIComponent(decodeURIComponent(value));
        args[key] = value;
    }
    return args;
};

注:中文解码必须写两层decodeURIComponent

4.5 调用客户端渲染

/**
 * 访问渲染
 * @param params
 * @param fn
 */
Convert.prototype.client = function (params, fn) {
    var page = require('webpage').create(); // 客户端
    var convert = this,
        external = this.external,
        render,
        output;

    /**
     *  渲染
     * @returns {*}
     */
    render = function () {
        switch (params.type) {
            case 'file':
                // 渲染图片
                page.render(params.outfile);
                return params.outfile;
            case 'base64':
            default:
                var base64 = page.renderBase64('PNG');
                return base64;

        }
    };

    /**
     * 输出
     * @param content 内容
     * @param success 是否成功
     */
    output = function (content, success) {
        if (params.server) {
            fn(content, success);
            page.close();
        } else {
            console.log(success ? "[SUCCESS]:" : "[ERROR]:" + content);
            page.close();
            convert.exit(params);// exit
        }
    };

    /**
     * 页面console监听
     * @param msg
     * @param lineNum
     * @param sourceId
     */
    page.onConsoleMessage = function (msg, lineNum, sourceId) {
        console.log(msg);
    };

    /**
     * 页面错误监听
     * @param msg
     * @param trace
     */
    page.onError = function (msg, trace) {
        output(msg, false); // 失败,返回错误信息
    };

    // 空白页
    page.open("about:blank", function (status) {
        // 注入依赖js包
        var hasJquery = page.injectJs(external.JQUERY3);
        var hasEchart = page.injectJs(external.ECHARTS3);
        var hasEchartChina = page.injectJs(external.ECHARTS_CHINA);

        // 检查js是否引用成功
        if (!hasJquery && !hasEchart) {
            output("Could not found " + external.JQUERY3 + " or " + external.ECHARTS3, false);
        }

        // 创建echarts
        page.evaluate(createEchartsDom, params);

        // 定义剪切范围,如果定义则截取全屏
        page.clipRect = {
            top: 0,
            left: 0,
            width: params.width,
            height: params.height
        };

        // 渲染
        var result = render();
        // 成功输出,返回图片或其他信息
        output(result, true);
    });
};

调用客户端,主要是为了开启沙盒,进行ECharts统计图创建以及图片渲染工作。

1. page.open("about:blank")一个空页面;
2. page.injectJs引入外部js,jQuery和ECharts;
3. page.evaluate页面创建ECharts的Dom,并生成统计图;
4. page.clipRect定义截图范围;
5. page.render渲染生成图片;
6. output输出,命令行直接将结果打印到控制台,Web将输出到response。

其中createEchartsDom是创建ECharts的Dom对象:

/**
 * 创建eCharts Dom层
 * @param params 参数
 */
function createEchartsDom(params) {
    // 动态加载js,获取options数据
    $('<script>')
        .attr('type', 'text/javascript')
        .html('var options = ' + params.opt)
        .appendTo(document.head);

    // 取消动画,否则生成图片过快,会出现无数据
    if (options !== undefined) {
        options.animation = false;
    }

    // body背景设置为白色
    $(document.body).css('backgroundColor', 'white');
    // echarts容器
    var container = $("<div>")
        .attr('id', 'container')
        .css({
            width: params.width,
            height: params.height
        }).appendTo(document.body);

    var eChart = echarts.init(container[0]);
    eChart.setOption(options);
}

以ECharts官方实例为例,上述代码等同于以下html:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>ECharts</title>
    <script src="jquery.min.js"></script>
    <script src="echarts.min.js"></script>
    <script type="text/javascript">
        // 指定图表的配置项和数据
        var options = {
            title: {
                text: 'ECharts示例'
            },
            tooltip: {},
            legend: {
                data:['销量']
            },
            xAxis: {
                data: ["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"]
            },
            yAxis: {},
            series: [{
                name: '销量',
                type: 'bar',
                data: [5, 20, 36, 10, 10, 20]
            }]
        };
        
        // 取消options动画效果
        if (options !== undefined) {
            options.animation = false;
        }
    </script>
</head>
<body style="background-color: white;">
    <!-- 为ECharts准备一个具备大小(宽高)的Dom -->
    <div id="container" style="width: 800px;height:400px;"></div>
    <script type="text/javascript">
        // 基于准备好的dom,初始化echarts实例
        var eChart = echarts.init(document.getElementById('container'));
        // 使用刚指定的配置项和数据显示图表。
        eChart.setOption(options);
    </script>
</body>
</html>

这里采用动态加载js来生成options,也可采用JSON.parse(options)将json串解析成对象,后期如果将json的验证完善后可以考虑采用此方式

4.6 Web服务返回信息

/**
 * 结果返回
 * @param data
 * @param success
 * @param msg
 */
Convert.prototype.serverResult = function (data, success, msg) {
    var result = {
        code: success ? 1 : 0,
        msg: undefined === msg ? success ? "success" : "failure" : msg,
        data: data
    };

    return JSON.stringify(result);
};

response.headersContent-Type设置为application/json;charset=utf-8,将返回json,并且为方便使用,将以统一的格式返回数据

其Json格式包括:

code:成功为1,失败为0;
msg: 成功为'success',失败为'failure'或其他错误信息;
data: 数据,正确返回base64字符串或图片路径,失败为空字符串

成功调用的json格式如下:

{
  "code":1,
  "msg":"success",
  "data": "base64 string"
}

4.7 异常信息处理

PhantomJS异常监听主要包括两种:web page模块异常和phantom对象异常

web page异常,此类异常最好手动处理,如果不处理,使用page.evaluate同步命令,如果出现异常PhantomJS线程将被阻止。

/**
 * 页面错误监听
 * @param msg
 * @param trace
 */
page.onError = function (msg, trace) {
    output("", false, msg); // 失败,返回错误信息
};

全局异常,此类异常在page.onError之后执行,一般发生无法预估异常。

/**
 * phantomJs 全局异常监听
 * @param msg
 * @param trace
 */
phantom.onError = function (msg, trace) {
    var msgStack = ['Convert ERROR: ' + msg];
    if (trace && trace.length) {
        msgStack.push('TRACE:');
        trace.forEach(function (t) {
            msgStack.push(' -> ' + (t.file || t.sourceURL) + ': ' + t.line + (t.function ? ' (in function ' + t.function + ')' : ''));
        });
    }
    console.error(msgStack.join('\n'));
    phantom.exit(1);
};

5. 脚本使用

1. 首先按照(1.1 PhantomJS下载安装)安装好PhantomJS;
2. 下载phantom.zip并将其解压;
3. 在`echarts-convert.js`同级目录下,运行命令` phantomjs echarts-convert.js -s `;
4. 如果控制台出现"echarts-convert server start success. [pid]=xxxx"则表示启动成功,默认端口9090;
5. Java通过HttpClient或URLConnection请求,url为http://localhost:9090;GET或POST请求,request参数为opt=optJson;

以HttpClient为例,java调用:

import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

public class EChartsConvertTest {

    public static void main(String[] args) {
         String url = "http://localhost:9090";
         // 不必要的空格最好删除,字符串请求过程中会将空格转码成+号
         String optJson = "{title:{text:'ECharts 示例'},tooltip:{},legend:{data:['销量']},"
         				+ "xAxis:{data:['衬衫','羊毛衫','雪纺衫','裤子','高跟鞋','袜子']},yAxis:{},"
         				+ "series:[{name:'销量',type:'bar',data:[5,20,36,10,10,20]}]}";
         Map<String, String> map = new HashMap<>();
         map.put("opt", optJson);
         try {
             String post = post(url, map, "utf-8");
             System.out.println(post);
         } catch (ParseException e) {
             e.printStackTrace();
         } catch (IOException e) {
             e.printStackTrace();
         }
     }
     
     // post请求
     public static String post(String url, Map<String, String> map, String encoding) throws ParseException, IOException {
         String body = "";
     
         // 创建httpclient对象
         CloseableHttpClient client = HttpClients.createDefault();
         // 创建post方式请求对象
         HttpPost httpPost = new HttpPost(url);
     
         // 装填参数
         List<NameValuePair> nvps = new ArrayList<>();
         if (map != null) {
             for (Entry<String, String> entry : map.entrySet()) {
                 nvps.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
             }
         }
         // 设置参数到请求对象中
         httpPost.setEntity(new UrlEncodedFormEntity(nvps, encoding));
     
         // 执行请求操作,并拿到结果(同步阻塞)
         CloseableHttpResponse response = client.execute(httpPost);
         // 获取结果实体
         HttpEntity entity = response.getEntity();
         if (entity != null) {
             // 按指定编码转换结果实体为String类型
             body = EntityUtils.toString(entity, encoding);
         }
         EntityUtils.consume(entity);
         // 释放链接
         response.close();
         return body;
     }
}

6. 问题与改进

目前整个脚本已经完成初版,但仍有一些已知或未知的问题和缺陷存在,主要包括以下几点:

  1. 命令行自定义模块使用commander.js,但因其是为nodejs开发的,所以只抽取了部分代码,有些改动未测试;
  2. PhantomJs的Web Server Module(Web服务器模块)仍处于实验性的功能模块,稳定性未知;
  3. ECharts的opt目前是人为拼接json字符串,这样会带来一些不必要的问题,后期需要规范传入json的格式;
  4. 目前只测试过较为简单的统计图,对于复杂的统计图并没有测试过,使用过程中可能会存在一些问题;
  5. 再每次渲染生成图片后执行page.close(),关闭页面,这样会稍微增加性能消耗,后续可以考虑只在沙盒里处理;
  6. 目前使用单线程处理图片,高并发下仍需要开发。

优化方向

  1. 增加json字符串即带引号的json支持;
  2. 通过java生成ECharts的opt json,而不是人为拼接字符串;
  3. 沙盒中处理ECharts图片,不再关闭page。

7. 参考资料

PhantomJS API

PhantomJS快速入门教程

Python爬虫利器四之PhantomJS的用法

java使用phantomJs抓取动态页面

JavaScript标准参考教程之PhantomJS

commander.js

ECharts官网

使用phantomJs服务器端导出Echarts图片

java调用phantomjs的性能问题

空文件

简介

使用PhantomJS在服务端生成ECharts图片 展开 收起
JavaScript
取消

发行版

暂无发行版

贡献者

全部

近期动态

不能加载更多了
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
JavaScript
1
https://gitee.com/saintlee/echartsconvert.git
git@gitee.com:saintlee/echartsconvert.git
saintlee
echartsconvert
EChartsConvert
master

搜索帮助