在pyecharts中将html导出为png使用phantomjs的方式

本文详细介绍了在使用PhantomJS和PyEcharts将Echarts图表转换为图片时遇到的一系列问题及其解决方案,包括图片过大、容器部署时的错误、字体缺失、远程JS引用问题以及背景颜色设置。通过压缩图片、环境变量配置、安装字体、本地化JS引用和修改图表配置等方法,成功解决了所有问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 安装phantomjs

  1. pip install snapshot-phantomjs
  2. 下载phantomjs安装包,参见:https://phantomjs.org/download.html,不同系统下载不同的包
  3. 将下载下来的包解压后的bin目录下的二进制文件放到你的$PATH中,查看$PATH的方式: echo $PATH,这里我是放到了/usr/local/bin下
    cp phantomjs-2.1.1-linux-x86_64/bin/phantomjs /usr/local/bin/phantomjs && chmod +x /usr/local/bin/phantomjs

2. 官方使用方法:

下面是官方的例子,我进行了一些小小的修改,允许执行宽和高,背景颜色,以及js的路径
from pyecharts import options as opts
from pyecharts.charts import Bar
from pyecharts.render import make_snapshot

from snapshot_phantomjs import snapshot

def bar_chart() -> Bar:
    c = (
        Bar(init_opts=opts.InitOpts(width=width, height=height, bg_color="white", js_host=file_path))
        .add_xaxis(["衬衫", "毛衣", "领带", "裤子", "风衣", "高跟鞋", "袜子"])
        .add_yaxis("商家A", [114, 55, 27, 101, 125, 27, 105])
        .add_yaxis("商家B", [57, 134, 137, 129, 145, 60, 49])
        .reversal_axis()
        .set_series_opts(label_opts=opts.LabelOpts(position="right"))
        .set_global_opts(title_opts=opts.TitleOpts(title="Bar-测试渲染图片"))
    )
    return c

make_snapshot(snapshot, bar_chart().render(), "bar.png")

3. 如果只是自己测试到这就算完成了,下面为部署到生产环境时遇到的一些问题

1. 我需要将生成的图片发送到企业微信中,但是生成的文件过大,企业微信最大允许发送2M的图片
  1. 先安装图像处理库pip3 install pillow
  2. 对图片进行压缩代码:
from PIL import Image
import os


def get_size(file):
    # 获取文件大小:KB
    size = os.path.getsize(file)
    return size / 1024


def get_outfile(infile, outfile):
    if outfile:
       return outfile
    dir, suffix = os.path.splitext(infile)
    outfile = '{}-out{}'.format(dir, suffix)
    return outfile


def compress_image(infile, outfile='', mb=150, step=10, quality=80):
    """不改变图片尺寸压缩到指定大小
    :param infile: 压缩源文件
    :param outfile: 压缩文件保存地址
    :param mb: 压缩目标,KB
    :param step: 每次调整的压缩比率
    :param quality: 初始压缩比率
    :return: 压缩文件地址,压缩文件大小
    """
    o_size = get_size(infile)
    if o_size <= mb:
        return infile
    while o_size > mb:
        im = Image.open(infile)
        im.save(outfile, quality=quality)
        if quality - step < 0:
            break
        quality -= step
        o_size = get_size(outfile)
    return outfile, get_size(outfile)
2. 线上是在容器中部署的服务,当执行生成图片时报错:
Auto configuration failed
140149858578048:error:25066067:DSO support routines:DLFCN_LOAD:could not load the shared library:dso_dlfcn.c:185:filename(libssl_conf.so): libssl_conf.so: cannot open shared object file: No such file or directory
140149858578048:error:25070067:DSO support routines:DSO_load:could not load the shared library:dso_lib.c:244:
140149858578048:error:0E07506E:configuration file routines:MODULE_LOAD_DSO:error loading dso:conf_mod.c:285:module=ssl_conf, path=ssl_conf
140149858578048:error:0E076071:configuration file routines:MODULE_RUN:unknown module name:conf_mod.c:222:module=ssl_conf
  • 解决方案:
    • export OPENSSL_CONF=/dev/null
    • 重新执行就可以生成图片了
3. 在本地发送图片没问题,到了容器中phantomjs将html转换成图片时乱码,缺少文字库
  • 解决方案:
    • 在centos中执行:
      yum install -y bitmap-fonts bitmap-fonts-cjk

    • 在ubuntu中执行:
      sudo apt-get install -y xfonts-wqy

4. 过了个国庆节回来发现代码没有任何变动但是不能用了,生成的html可以打开,但是转换成图片就报错:“ReferenceError: Can’t find variable: echarts\n\n undefined:1\nnull\n”
  • 排错:
    • 搜索有人说是需要代码的执行路径中不能包含中文,但是我本身的执行路径是没有中文的,
    • 打开html文件发现echarts的引用是通过src引用的远程地址,猜测是否是远端拉不下来,但是在本地和服务器都是可以打开的,这个也被否定了
    • 一度陷入僵局,谷歌并没有什么有用的信息
    • 突然想起以前翻源码的时候在opts.InitOpts里面可以填写js_host的地址,那么我把js下载下来是否可行呢,试了下还真可以,问题就解决了,但是为啥使用远程引用的不可以有点不太理解
  • 解决方案:
    • 1.先把远程引用的js下载到本地,和执行的文件放在同一目录下
      wget https://assets.pyecharts.org/assets/echarts.min.js
    • 2.修改配置信息生成js引用地址的时候使用本地的js
file_path = "{}/".format(os.path.dirname(os.path.abspath(__file__)))
Bar(init_opts=opts.InitOpts(width=width, height=height, bg_color="white", js_host=file_path)))

这里主要看你哪里用的init_opts这个参数,没有的话可以在实例化图像实例的时候加上这个参数
- 3.重新执行图片就生成出来了

5. 生成的图片的背景是一堆的灰色方格,想要修改图片的背景颜色怎么办呢?
  • 在生成html的时候就要进行修改,指定背景颜色,同样是
    Bar(init_opts=opts.InitOpts(width=width, height=height, bg_color="white", js_host=file_path)))
    里面的bg_color就是背景颜色,我这里指定的是白色

至此问题全部解决,Bingo.

### Java 实现 ECharts 图表导出至 Word 文档 为了实现在Java项目中将ECharts图表导出到Word文档的功能,可以采用多种方法和技术栈组合来完成这一目标。下面提供了一种基于Spring Boot框架下的解决方案,该方案利用了Apache POI库用于创建和操作Word文件,并通过无头浏览器(如PhantomJS或Puppeteer)获取ECharts图表的静态图像。 #### Maven依赖配置 首先,在项目的`pom.xml`文件中加入必要的Maven依赖项以支持Apache POI以及Web客户端: ```xml <dependencies> <!-- Apache POI --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.2.3</version> </dependency> <!-- Selenium WebDriver for headless browser control --> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-java</artifactId> <version>4.1.0</version> </dependency> <!-- ChromeDriver dependency, adjust version as needed --> <dependency> <groupId>com.github.webdriverextensions</groupId> <artifactId>webdrivermanager</artifactId> <version>5.0.0</version> </dependency> </dependencies> ``` #### Controller层处理逻辑 定义一个控制器接收前端发送过来的数据并调用服务层进行实际业务处理: ```java @RestController @RequestMapping("/export") @CrossOrigin(origins = "*") public class ExportController { private final IEchartsExportService exportService; @Autowired public ExportController(IEchartsExportService exportService){ this.exportService = exportService; } @PostMapping(value="/to-word", consumes="application/json", produces="text/plain;charset=UTF-8") public ResponseEntity<String> exportToWord(@RequestBody Map<String,Object> chartData) { try{ File wordFile = exportService.generateChartInWord(chartData); return ResponseEntity.ok().body("文件已生成:" + wordFile.getAbsolutePath()); }catch(Exception ex){ return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ex.getMessage()); } } } ``` #### Service接口与实现类 设计服务接口及其具体实现负责核心功能——即根据传入参数构建ECharts实例并将之转换成图片形式嵌入Word文档内: ```java // 定义服务接口 public interface IEchartsExportService { File generateChartInWord(Map<String, Object> chartOptions); } @Service public class EchartsExportServiceImpl implements IEchartsExportService { @Override public File generateChartInWord(Map<String, Object> chartOptions) throws Exception { // 创建临时HTML页面存储ECharts配置信息 String htmlContent = "<!DOCTYPE html><html lang='en'><head>" + "<meta charset='UTF-8'>" + "<title>Echart Chart</title>" + "</head><body>" + "<div id='main' style='width: 600px;height:400px;'></div>" + "<script src='https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js'></script>" + "<script type='text/javascript'>var myChart = echarts.init(document.getElementById('main'));myChart.setOption("+ JSON.toJSONString(chartOptions)+");</script></body></html>"; Path tempHtmlPath = Files.createTempFile(null,".html"); Files.write(tempHtmlPath, htmlContent.getBytes()); // 使用Selenium启动Chrome无界面模式加载上述HTML资源 System.setProperty("webdriver.chrome.driver","path/to/chromedriver.exe"); WebDriver driver = new ChromeDriver(new ChromeOptions().addArguments("--headless")); driver.get(tempHtmlPath.toUri().toString()); // 获取截图作为PNG格式字节数组 byte[] screenshotBytes = ((TakesScreenshot)driver).getScreenshotAs(OutputType.BYTES); // 关闭驱动程序释放资源 driver.quit(); // 将字节流转存为本地图片文件供后续插入Word使用 BufferedImage bufferedImage = ImageIO.read(new ByteArrayInputStream(screenshotBytes)); Path imageFilePath = Files.createTempFile(null,".png"); ImageIO.write(bufferedImage, "png",imageFilePath.toFile()); // 构建XWPFDocument对象并向其中添加带有ECharts图表的内容 XWPFDocument document = new XWPFDocument(); XWPFParagraph paragraph = document.createParagraph(); XWPFPicture picture = paragraph.createPicture(imageFilePath.toString(),document.getNextPicNameNumber(PIC_TYPE_PNG)); // 设置段落样式使图表居中显示 CTP ctp = paragraph.getCTP(); CTJc ctjc = ctp.addNewPPr().isSetJc() ? ctp.getPPr().getJc() : ctp.addNewPPr().addNewJc(); ctjc.setVal(STJc.CENTER); // 输出最终形成的Word文档给用户下载 Path outputDocxPath = Files.createTempFile(null,".docx"); FileOutputStream outStream = new FileOutputStream(outputDocxPath.toFile()); document.write(outStream); outStream.close(); document.close(); return outputDocxPath.toFile(); } } ``` 此代码片段展示了如何在一个典型的Spring Boot应用程序环境中设置RESTful API端点,以便接受JSON格式化的ECharts选项并通过一系列步骤将其转化为可读取的Word (.docx) 文件[^2][^4]。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值