使用phantomJS制作图片测试报告

之前在盘古的时候,每周值班的QA都要发送一个周版本测试报告邮件,长的大概是这个样子:

                                       

报告包含一些说明和统计信息,数据需要值班QA到各个地方搜集,然后写到邮件里。
有一天,某老大想到,这东西能不能做的好看一点,来点图表什么的,再来个一键发送?
于是N天后,高大上的网页版报告出现啦,下图是其中一小部分(关键部分已打码)
这个时候,问题来了,测试报告是要发邮件的,但邮箱会对JS进行过滤,什么<Script>、<iframe>都给你删的干干净净(这就是为啥广告邮件都这么挫)。此外,邮件一般会用客户端查看,但是其内置的浏览器核心往往很"经典",例如闪电邮就是某个古老的IE版本。ps:邮箱大师win版的显示效果比闪电邮更...不好看。
这个报告包含了大量的图表,因此通常采用的,给邮件专门制作一个简版页面的方案是不可行的。想来想去就只剩下网页截图这一种方案。
调研了一番,首先找到的解决方案是"使用PyQt4的QtWebKit对整个网页截图":
#-*- coding:utf-8 -*-
import sys
import os.path
from PyQt4 import QtGui,QtCore,QtWebKit
 
class PageShotter(QtGui.QWidget):
    def __init__(self,url,parent=None):
        QtGui.QWidget.__init__(self,parent)
        self.url = url
         
    def shot(self):
        webView = QtWebKit.QWebView(self)
        webView.load(QtCore.QUrl(self.url))
        self.webPage = webView.page()
        self.connect(webView,QtCore.SIGNAL("loadFinished(bool)"),self.savePage)
         
    def savePage(self,finished):
         if finished:
            print u"开始截图!"
            size = self.webPage.mainFrame().contentsSize()
            print u"页面宽:%d,页面高:%d" % (size.width(),size.height())
            self.webPage.setViewportSize(QtCore.QSize(size.width()+16,size.height()))
            img = QtGui.QImage(size, QtGui.QImage.Format_ARGB32)
            painter = QtGui.QPainter(img)
            self.webPage.mainFrame().render(painter)
            painter.end()
            fileName= "shot.png";
            if img.save(fileName):
                filePath = os.path.join(os.path.dirname(__file__), fileName)
                print u"截图完毕:%s" % filePath
            else:
                print u"截图失败";
        else:
            print u"网页加载失败!"
        self.close()
         
if __name__=="__main__":
    app = QtGui.QApplication(sys.argv)
    shotter = PageShotter("http://crystalpot.cn/menu/0")
    shotter.shot()
    sys.exit(app.exec_())
代码很简单,开始用着也很舒爽,但是马上就遇到了一个问题,有时截图中某些图表还没有加载完毕。
上面的代码中,是使用QtCore.SIGNAL("loadFinished(bool)") 来控制截图时机的,要增加截图前的等待时间,就需要学习一下QT的信号槽机制。但是,搜集了一下,发现相关资料不那么友好,再加上QTWebKit的解析效果也不是很完美,于是这个方案PASS。
然后继续找,就发现了PhantomJS:
PhantomJS 是一个基于 WebKit 的服务器端 JavaScript API。它全面支持web而不需浏览器支持,其快速,原生支持各种Web标准: DOM 处理, CSS 选择器, JSON, Canvas, 和 SVG。 PhantomJS 可以用于 页面自动化,网络监测,网页截屏,以及无界面测试等。安装包下载地址:http://phantomjs.org/download.html ,提供 Windows,Mac OS,Linux版本。
                                                                 
linux版本解压后的目录如上图,examples里有很多示例的js文件,在bin/执行"phantomjs xx.js"即可,截图的js代码如下:
var page = require('webpage').create(), address, output, size;
system = require('system');

 if (system.args.length != 3) {
 // 不带参数,即输入 phantomjs resize_webshot.js
    console.log('less');
    address = 'http://www.xxx.com';
    output = 'example.png';
} else {
    console.log('more');
    address = system.args[1];    // 需要截图的url
    output = system.args[2];     // 截图保存路径
}

page.viewportSize = { width: 1680, height: 1050 };   //解析分辨率建议设的大一点

page.open(address, function (status) {
 //通过在页面上执行脚本获取页面的渲染高度
var bb = page.evaluate(function () {
    return document.getElementsByTagName('html')[0].getBoundingClientRect();
});

// 按照实际页面的高度,设定渲染的宽高;这里的数值需要仔细调整
page.clipRect = {
    top:    bb.top+1,
    left:   bb.left+425,
    width:  bb.width-850,      
    height: bb.height-60
};

//预留一定的渲染时间
window.setTimeout(function() {
    page.render(output);
    page.close();
    console.log('render ok');

    var fs = require('fs');
    var ss = output.split('/');
    var pic = ss[ss.length-1];
    var k = output.lastIndexOf('/');
    var prefix = './';
    if (k != -1){
        prefix = output.substring(0, k+1);
    }
    //在截图完成后创建一个标志文件;无bug时可以删掉这两行
    var file = fs.open(prefix+ 'done_'+ pic, 'w');
    file.close();
    phantom.exit();
}, 1000);
});
代码同样非常简单,看注释即可,这里就不细说了。生成的截图大约300~500KB。
在使用过程中,发现了两个坑点:
1.在flask搭的网站中,os.system("phantomjs xx.js")调用截图时,一直无法返回,直到访问超时后才截图成功,在Python命令行里输入这个指令却没有问题。尝试了很多解决方案都无效,最后只得把指令改成"phantomjs xx.js &",即把调用截图的操作改成异步,然后改写js,在截图完成后创建一个标志文件,根据这个文件是否生成判断是否截图完毕。
2.linux下的中文字体问题。服务器的系统是Debian 3.2,截图时发现默认没有安装中文字体,安装雅黑后发现显示效果很差,相比windows细了很多。宋体、思源黑体等效果也都不好。一番尝试后,发现这方面还得看苹果,苹方字体(ios9默认字体?)显示效果最佳,最后截图里的效果如下:
ps:相应的,苹方字体在windows下简直无法直视...
本文来自网易实践者社区,经作者hzsunzhengyu授权发布。