最近刚接触PhantomJS, 据说这工具是一个基于WebKit的服务器端JavaScript API,能够实现绝大部分浏览器的操做, 火烧眉毛就想练练手.因而就简单写了一个程序, 简单介绍下:javascript
需求: 经过phantomJS向一个网站发起请求, 而且记录各资源加载的时间,名字 思路: 1.经过 onResourceRequested,得到资源请求时间t1, 并经过资源ID,记录在关联数组内 2.经过 onResourceReceived 得到资源响应时间t2, 也一样存到关联数组内 2.用t2 - t1得出的, 就是资源加载花费的毫秒值 3.由于资源ID是相关联的,因此只须要取任意一个关联组的url就能够
test.js 内容以下:css
// 加载库 var page = require('webpage').create(); var system = require('system'); var req = {}; // 请求关联数组 var res = {}; // 响应关联数组 var num = 0; // 用于记录资源数 var address = 'http://www.yaochufa.com'; // 在 onResourceRequested 事件绑定动做 page.onResourceRequested = function(request){ num++; req[request.id] = {'url': request.url, 'time': request.time}; }; // 在 onResourceReceived 事件绑定动做 page.onResourceReceived = function(response){ res[response.id] = {'url': response.url, 'time': response.time}; }; // 开始请求 page.open(address, function(status){ if(status !== 'success') { // 若是请求成功, 退出 console.log(status); phantom.exit(); } else{ console.log('主页加载完毕'); } // 根据前面记录的资源请求个数, 开始统计各资源的加载时间 for(i=1;i<=num;i++) { // 取出请求数组的资源开始时间, 并转换成时间戳 var req_time = new Date(req[i]['time']).getTime(); // 取出响应数组的资源结束时间, 并转换成时间戳 var res_time = new Date(res[i]['time']).getTime(); // 做差就是加载的时间 var diff = res_time - req_time; console.log('ID:s',i, 'Loadtime:', diff, 'ms'); // 打印对应ID的url console.log(res[i]['url']); } phantom.exit(); });
phantomjs test.js # 结果输出: ReferenceError: Can't find variable: sfCode http://qiniu-cdn7.jinxidao.com/assets/js/index.js?v=201704201643:1 主页加载完毕 ID:s 1 Loadtime: 73 ms http://www.yaochufa.com/ ID:s 2 Loadtime: 50 ms http://qiniu-cdn7.jinxidao.com/assets/css/common.css?v=201704201643 ID:s 3 Loadtime: 54 ms http://qiniu-cdn7.jinxidao.com/assets/css/index.css?v=201704201643 ID:s 4 Loadtime: 80 ms http://qiniu-cdn7.jinxidao.com/assets/js/jquery.js ID:s 5 Loadtime: 78 ms http://qiniu-cdn7.jinxidao.com/assets/js/lazyload.js ID:s 6 Loadtime: 78 ms http://qiniu-cdn7.jinxidao.com/assets/js/common.js?v=201704201643 ID:s 7 Loadtime: 150 ms http://qiniu-cdn7.jinxidao.com/assets/js/index.js?v=201704201643 ID:s 8 Loadtime: 79 ms http://qiniu-cdn7.jinxidao.com/js/user_analytics_pc.js?v=201704201643 ID:s 9 Loadtime: 154 ms http://qiniu-cdn7.jinxidao.com/js/zhugeio_config.js?v=201704201643 ID:s 10 Loadtime: 106 ms http://cdn7.jinxidao.com/images/icon.png?v=1.3 ID:s 11 Loadtime: 104 ms http://cdn.jinxidao.com/images/icon/photo-app.png ID:s 12 Loadtime: 100 ms http://static.anquan.org/static/outer/image/hy_124x47.png ID:s 13 Loadtime: 91 ms http://cdn7.jinxidao.com/www/images/loading.gif ID:s 14 Loadtime: 91 ms http://cdn7.jinxidao.com/images/logo.png ID:s 15 Loadtime: 94 ms http://cdn7.jinxidao.com/www/images/weixin_code.png ID:s 16 Loadtime: 92 ms http://cdn7.jinxidao.com/www/images/footer-links.png ID:s 17 Loadtime: 94 ms http://cdn7.jinxidao.com/www/images/weixin_code.png?v=1.0 ID:s 18 Loadtime: 91 ms http://cdn7.jinxidao.com/www/images/web-trust/alipay.png ID:s 19 Loadtime: 91 ms http://cdn7.jinxidao.com/www/images/web-trust/zhizhao.jpg ID:s 20 Loadtime: 93 ms http://cdn7.jinxidao.com/www/images/web-trust/chengxin.jpg ID:s 21 Loadtime: 94 ms http://cdn7.jinxidao.com/www/images/web-trust/kexin.jpg ID:s 22 Loadtime: 87 ms http://cdn.jinxidao.com/images/white_bg.png ID:s 23 Loadtime: 91 ms http://cdn.jinxidao.com/detail/appCode.png ID:s 24 Loadtime: 94 ms http://cdn.jinxidao.com/detail/appCode.png?v=3 ID:s 25 Loadtime: 64 ms http://www.yaochufa.com/ajax/checkcity?v=1493041417771&callback=jQuery1113015539110638201237_1493041417766&_=1493041417767 ID:s 26 Loadtime: 64 ms http://tj.yaochufa.com/kafkahttp/kafka/log?callback=jQuery1113015539110638201237_1493041417768&event_content=http%3A%2F%2Fwww.yaochufa.com%2F&log_id=1&app_id=4&longitude=&latitude=&user_id=¤t_citycode=&upload_time=2017-04-24+21%3A43%3A37&event_time=1493041418&session_id=1493041418¤t_url=http%3A%2F%2Fwww.yaochufa.com%2F&from_url=&use_agent=Mozilla%2F5.0+(Unknown%3B+Linux+x86_64)+AppleWebKit%2F538.1+(KHTML%2C+like+Gecko)+PhantomJS%2F2.1.1+Safari%2F538.1&visit_ip=114.55.70.153&cookie=38e73c28-09f1-2945-47d8-cbd70f3d72ff&arg2=&event_id=700000&_=1493041417769 ID:s 27 Loadtime: 73 ms http://sdk.zhugeio.com/zhuge-lastest.min.js?v=2017324 ID:s 28 Loadtime: 136 ms http://hm.baidu.com/hm.js?84c5b2688d39b4e3c23d132b53b4e79b ID:s 29 Loadtime: 151 ms https://apipool.zhugeio.com/web_event/?method=web_event_srv.upload&event=%7B%22type%22%3A%20%22statis%22%2C%22sdk%22%3A%20%22web%22%2C%22sdkv%22%3A%20%221.3.0%22%2C%22cn%22%3A%20%22web%22%2C%22vn%22%3A%20%221.0%22%2C%22ak%22%3A%20%22b744d4b142c942c09cdc5095ba060824%22%2C%22did%22%3A%20%2215ba0340eac38a-04e727ec2-273f781b-c0000-15ba0340ead40b%22%2C%22ts%22%3A%201493041417.903%2C%22debug%22%3A%201%2C%22data%22%3A%20%5B%0A%20%20%20%20%7B%22et%22%3A%20%22ss%22%2C%22sid%22%3A%201493041417.902%2C%22cn%22%3A%20%22web%22%2C%22vn%22%3A%20%221.0%22%2C%22pr%22%3A%20%7B%22os%22%3A%20%22Linux%22%2C%22br%22%3A%20%22Safari%22%2C%22rs%22%3A%20%221024*768%22%2C%22url%22%3A%20%22http%3A%2F%2Fwww.yaochufa.com%2F%22%7D%7D%0A%5D%7D&_=1493041417904 卡住...
前面的输出, 咱们已经看到已经按照咱们需求那样, 得出资源ID, 资源加载时间, 资源URL, 可是很奇怪的事, 到了大概是30的时候, 就卡住了, 这里确定不是程序运行完, 由于程序结尾有个退出, 若是是正常结束了, 应该就退出了.因此我以为这个确定有哪里出错了! 这也是让我以为phantomJS很很差的地方, 报错也不给个明显的错误提示, 就在哪里一动不动的.
既然phantomJS不明显报错, 我们只能一步步调试了, 以前谷歌上看到, phantomJS可以用debug模式运行, 而后经过访问特定端口来用浏览器调试, 可是在这不行, 由于浏览器压根打不开那个连接, 一直在转, 也不知道是什么缘由. 因此咱们这里只能用console.log来排错了!
由于打印输出的代码是在最后面的, 那么能够证实前面的事件是正确被执行的, 由于若是前面的事件失败了, 那么整个程序确定是不会打印的,相反的而是会停在前面卡住了.
为了证实这一点, 我们把for循环去掉, 源码再也不重复, 直接贴结果:java
[root@iZ23pynfq19Z phantomjs-2.1.1-linux-x86_64]# ./bin/phantomjs 4.js ReferenceError: Can't find variable: sfCode http://qiniu-cdn7.jinxidao.com/assets/js/index.js?v=201704201643:1 主页加载完毕
嗖的一声就结束了, 这就验证了咱们的猜测, 出错的代码应该是在for的打印里面, 既然知道大概的访问, 我们for里面的每句代码都注释掉, 一句句单独执行, for循环体代码分别是:jquery
for(i=1;i<=num;i++) { // 取出请求数组的资源开始时间, 并转换成时间戳 var req_time = new Date(req[i]['time']).getTime(); } phantom.exit(); ---- 试验1结果: 程序顺利输出并结束 ---- for(i=1;i<=num;i++) { // 取出响应数组的资源结束时间, 并转换成时间戳 var res_time = new Date(res[i]['time']).getTime(); } phantom.exit(); ---- 试验2结果: 程序卡住了!! ----
能够看出, 问题应该是出如今第二句获取时间的地方, 可是这里应该是没问题的, 由于在实验时, 别的时间都能顺利转换! 可是没办法, 只有试验才能给出证据! 咱们将这句话拆分两句运行linux
for(i=1;i<=num;i++) { // 取出响应数组的资源结束时间, 并转换成时间戳 var res_time = new Date(res[i]['time']) console.log(res_time); console.log(res_time.getTime()); } phantom.exit(); ## 程序输出: ... Mon Apr 24 2017 22:18:58 GMT+0800 (CST) 1493043538945 Mon Apr 24 2017 22:18:58 GMT+0800 (CST) 1493043538932 Mon Apr 24 2017 22:18:58 GMT+0800 (CST) 1493043538946 Mon Apr 24 2017 22:18:59 GMT+0800 (CST) 1493043539087 卡住了...
看来问题真的出如今这问题, 可是这样仍是不能看出问题啊, 由于变量都正常输出了! 思来想去都以为找不到问题, 只能继续往上拆了, 它是经过i在响应关联数组取出对象, 因此咱们在上面加一句打印对象:git
for(i=1;i<=num;i++) { // 取出响应数组的资源结束时间, 并转换成时间戳 console.log(res[i]['time']); var res_time = new Date(res[i]['time']) console.log(res_time); console.log(res_time.getTime()); } phantom.exit(); ## 程序输出: ... Mon Apr 24 2017 22:26:21 GMT+0800 (CST) 1493043981129 {"url":"https://apipool.zhugeio.com/web_event/?method=web_event_srv.upload&event=%7B%22type%22%3A%20%22statis%22%2C%22sdk%22%3A%20%22web%22%2C%22sdkv%22%3A%20%221.3.0%22%2C%22cn%22%3A%20%22web%22%2C%22vn%22%3A%20%221.0%22%2C%22ak%22%3A%20%22b744d4b142c942c09cdc5095ba060824%22%2C%22did%22%3A%20%2215ba05b2b444b7-0c599ec6d-273f781b-c0000-15ba05b2b454b1%22%2C%22ts%22%3A%201493043981.128%2C%22debug%22%3A%201%2C%22data%22%3A%20%5B%0A%20%20%20%20%7B%22et%22%3A%20%22ss%22%2C%22sid%22%3A%201493043981.126%2C%22cn%22%3A%20%22web%22%2C%22vn%22%3A%20%221.0%22%2C%22pr%22%3A%20%7B%22os%22%3A%20%22Linux%22%2C%22br%22%3A%20%22Safari%22%2C%22rs%22%3A%20%221024*768%22%2C%22url%22%3A%20%22http%3A%2F%2Fwww.yaochufa.com%2F%22%7D%7D%0A%5D%7D&_=1493043981128","time":"2017-04-24T14:26:21.273Z"} Mon Apr 24 2017 22:26:21 GMT+0800 (CST) 1493043981273 Mon Apr 24 2017 22:26:21 GMT+0800 (CST) 1493043981132 undefined 程序卡住..
惊喜发现undefined, 嗯! 应该是undefined没有getTime方法, 致使程序出错了.看了下对应的资源ID是30, 而对应的url是:https://apipool.zhugeio.com, 不过为何这个对象是undefined呢? 而请求资源那块确实是有的, 谷歌了下, 发现这个是一个资源监控网站, 也就是相似帮别人测试资源加载速度的, 难道这个请求是没有返回的? 先无论, 既然它是undefined. 那就经过判断过滤掉吧:web
for(i=1;i<=num;i++) { // 取出请求数组的资源开始时间, 并转换成时间戳 var req_time = new Date(req[i]['time']).getTime(); // 过滤undefned if(res[i]) { // 取出响应数组的资源结束时间, 并转换成时间戳 var res_time = new Date(res[i]['time']).getTime(); // 做差就是加载的时间 var diff = res_time - req_time; console.log('ID: ',i, 'Loadtime:', diff, 'ms'); // 打印对应ID的url console.log(res[i]['url']); } else { console.log('ID: ' + i + ' no response'); } } --- 程序输出 --- ..... ID: 30 no response ID: 31 Loadtime: 148 ms https://apipool.zhugeio.com/web_event/?method=web_event_srv.upload&event=%7B%22type%22%3A%20%22statis%22%2C%22sdk%22%3A%20%22web%22%2C%22sdkv%22%3A%20%221.3.0%22%2C%22cn%22%3A%20%22web%22%2C%22vn%22%3A%20%221.0%22%2C%22ak%22%3A%20%22b744d4b142c942c09cdc5095ba060824%22%2C%22did%22%3A%20%2215ba0698d3a9f4-0797b7d87-273f781b-c0000-15ba0698d3b4b2%22%2C%22ts%22%3A%201493044923.712%2C%22debug%22%3A%201%2C%22data%22%3A%20%5B%0A%20%20%20%20%7B%22et%22%3A%20%22info%22%2C%22pr%22%3A%20%7B%22os%22%3A%20%22Linux%22%2C%22br%22%3A%20%22Safari%22%2C%22rs%22%3A%20%221024*768%22%2C%22url%22%3A%20%22http%3A%2F%2Fwww.yaochufa.com%2F%22%2C%22cn%22%3A%20%22web%22%2C%22vn%22%3A%20%221.0%22%7D%7D%0A%5D%7D&_=1493044923712 ID: 32 Loadtime: 37 ms .....
能够看到, 这个脚本能够正常运行了.
不过虽然能够运行了, 仍是很好奇是否是真的有资源只有请求, 而没有响应的, 然而事实并非! 在后面的测试中, 我发现, 若是咱们在onResourceReceived打印资源ID和对象时, 会发现, 我们前面丢失的30号对象, 也是有输出的, 我们来试下:ajax
page.onResourceReceived = function(response){ console.log('ID: ' + response.id); // 将对象转变成JSON字符串 console.log(JSON.stringify(response)); res[response.id] = {'url': response.url, 'time': response.time}; }; --- 程序输出 --- ....(前面打印耗时) // 单独打印 ID: 30 {"contentType":null,"headers":[],"id":30,"redirectURL":null,"stage":"end","status":null,"statusText":null,"time":"2017-04-24T14:52:08.402Z","url":"https://api.zhugeio.com/v1/events/codeless/appkey/b744d4b142c942c09cdc5095ba060824/platform/3?event_url=%2F"} ...
能够看出, 资源ID:30是有响应的, 只是响应的比较慢而已, 当开始运行循环体时, 它还没完成写, 由于JS时众所周知的异步编程, 因此它并不像咱们通常程序那样顺序执行, 而是经过回调的方式完成任务.编程
既然知道它也是有响应的的, 那么我们就不能抛弃它! 由于它也是咱们的一份子! 可是咱们该怎么作呢? 无奈之下去看PhantomJS的官网, 看到一个示范例子里面用到一个事件:onLoadFinished, 字面意思就是完成加载时, 这个看起来就是咱们要找的, 由于若是页面加载完毕, 那么资源方面确定是已经收齐(菜鸟理解), 那咱们开始改造刚才的脚本的, 将脚本的for分离出来, 放到 onLoadFinished 事件中, 代码以下:segmentfault
page.onLoadFinished = function(status){ console.log('加载完毕, 状态: ' + status); for(var i=1;i<=num;i++) { var req_time = new Date(result[i].time).getTime(); if (rest[i]) { var rest_time = new Date(rest[i].time).getTime(); var diff = rest_time - req_time; console.log('ID:s',i, 'Loadtime:', diff, 'ms'); } else // 颜色提醒 console.log('\033[31mNo such response!\033[0m'); console.log(result[i].url); } };
通过上面的改造, 脚本已经可以比较好的算出加载时间了,不过我们有些地方还须要优化, 那就是输入地址那边, 由于地址是写死的, 因此若是咱们用来测试别的站点时, 就必需要修改代码, 这简直就是一个大错误! 因此咱们须要system库!, 完整代码以下:
// 加载库 var page = require('webpage').create(); var system = require('system'); var req = {}; // 请求关联数组 var res = {}; // 响应关联数组 var num = 0; // 用于记录资源数 var args = system.args; if (args.length !== 2) { console.log('\033[31mUsage: Url ( http://www.baidu.com )\033[0m'); phantom.exit(); } var address = args[1]; // 在 onResourceRequested 事件绑定动做 page.onResourceRequested = function(request){ num++; req[request.id] = {'url': request.url, 'time': request.time}; }; // 在 onResourceReceived 事件绑定动做 page.onResourceReceived = function(response){ res[response.id] = {'url': response.url, 'time': response.time}; }; page.onLoadFinished = function(status){ console.log('加载完毕, 状态: ' + status); // 根据前面记录的资源请求个数, 开始统计各资源的加载时间 for(var i=1;i<=num;i++) { var req_time = new Date(req[i].time).getTime(); if (res[i]) { var rest_time = new Date(res[i].time).getTime(); var diff = rest_time - req_time; console.log('ID:s',i, 'Loadtime:', diff, 'ms'); } else console.log('\033[31mNo such response! ID: ' + i + '\033[0m'); console.log(req[i].url); } }; // 开始请求 page.open(address, function(status){ if(status !== 'success') { // 若是请求成功, 退出 console.log(status); phantom.exit(); } else{ console.log('主页加载完毕'); phantom.exit(); } });
不过仍是有时候仍是会出现找不到那个响应慢的请求, 可能须要换种实现的思路或者更加贴近的事件, 不过这个用来练手真是感受, 以前一直以为PhantomJS卡死, 其实只是程序错了, 此次对phantomJS改观了, 这是一个不错的工具, 日后还会继续深刻学习!
欢迎各位大神指点交流,转载请注明来源:https://segmentfault.com/a/11...