文档章节

Web 自动化测试与智能爬虫利器:PhantomJS 简介与实战

大数据之路
 大数据之路
发布于 2015/04/28 03:55
字数 2346
阅读 27444
收藏 109

估计部分同学没听过这个工具,那先简单介绍下它的背景与作用。

1、PhantomJS 是什么?

PhantomJS是一个基于WebKit的服务器端JavaScript API,它无需浏览器的支持即可实现对Web的支持,且原生支持各种Web标准,如DOM 处理、JavaScript、CSS选择器、JSON、Canvas和可缩放矢量图形SVG。PhantomJS主要是通过JavaScript和CoffeeScript控制WebKit的CSS选择器、可缩放矢量图形SVG和HTTP网络等各个模块。PhantomJS主要支持Windows、Mac OS、Linux三个平台,并且提供了对应的二进制安装包。

PhantomJS 的使用场景如下:

  • 无需浏览器的Web测试:无需浏览器的情况下进行快速的Web测试,且支持很多测试框架,如YUI Test、Jasmine、WebDriver、Capybara、QUnit、Mocha等。

  • 页面自动化操作:使用标准的DOM API或一些JavaScript框架(如jQuery)访问和操作Web页面。

  • 屏幕捕获:以编程方式抓起CSS、SVG和Canvas等页面内容,即可实现网络爬虫应用。构建服务端Web图形应用,如截图服务、矢量光栅图应用。

  • 网络监控:自动进行网络性能监控、跟踪页面加载情况以及将相关监控的信息以标准的HAR格式导出。

PhantomJS 已形成了一个功能非常强大的生态圈内容,相关项目如下:

  • CasperJS:一个开源的导航脚本处理和高级测试工具

  • Poltergeist :测试工具Capybara的测试驱动

  • Guard::Jasmine:能够基于Rails实现自动化测试Jasmine的Specs

  • GhostDriver:远程 WebDriver 有线协议的开源实现

  • PhantomRobot:PhantomJS机器人测试框架

  • Mocha-PhantomJS:JavaScript测试框架Mocha的客户端

此外,生态圈还包括基于PhantomJS实现了众多截屏工具,如capturejs、pageres、phantomjs-screenshots、manet、screenshot-app等;以及Node.js、Django、PHP、Sinatra等语言的截图API和Confess、GhostStory、Grover等众多工具。

PhantomJS当前最新版本是2.0,目前除了 Linux 的二进制版本未发布之外,其它跨平台的版本都发布了二进制与源码包可供选择,本文所用的测试环境来源于 Windows 二进制 2.0 版本。

2、PhantomJS VS Selenium 

去年在《WEB 自动化测试工具 Selenium 简介及其应用》一文中介绍过 Selenium 的用法与功能,其实它也是一个 Web 自动化测试工具,是 ThoughtWorks专门为Web应用程序编写的一个验收测试工具。Selenium测试直接运行在浏览器中,就像真正的用户在操作一样。支持的浏览器包括IE(7、8、9)、Mozilla Firefox、Mozilla Suite等。这个工具的主要功能包括:测试与浏览器的兼容性——测试你的应用程序看是否能够很好得工作在不同浏览器和操作系统之上。测试系统功能——创建衰退测试检验软件功能和用户需求。支持自动录制动作和自动生成 .Net、Java、Perl等不同语言的测试脚本。

用过的同学估计都有感受,就是这货本质上是依赖于浏览器的,每一步操作都是直接操纵图形化的浏览器,这样无论是从性能还是可编程性上来说都差多了,而今天介绍的 PhantomJS 则不然,它除了拥有 Selenium 的绝大部分功能之外,更强大的地方在于他是一个“无头浏览器”,没有图形化界面,直接面向程序 API 接口,性能和可操作性比 Selenium 高了很多。这两个工具最重要的就是能执行页面 JS,现在流行的基本如下几种:

  • QtWebKit,已知有 Python 和 C++ 支持

  • PhantomJS,已知有 JavaScript、CoffeeScript 和 Python 支持,也是 Webkit 内核

  • SlimerJS,已知有 JavaScript 支持,Gecko 内核,和火狐是一样的,也可以运行于火狐之上

  • CasperJS,已知有 JavaScript 支持。上边两个的进一步封装

这个重要的特性使得他们和一些爬虫框架组合起来使用之后,目测一大波智能爬虫正向我们走来~    -_-|||

3、实战:抓取某个页面所有的子请求

简单的入门教程这里就不说了,可以参考官方文档或者文末链接,假设我们现在有个需求,需要抓取、分析某个页面加载时浏览器发起的所有的子请求,效果如下如所示:

其实,这个功能 phantomjs examples 的 netlog.js 已经实现了,但是官方的例子在网络不好、页面复杂的时候容易漏掉请求,我这里稍作了修改:

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

if (system.args.length === 1) {
    console.log('Usage: netlog.js <some URL>');
    phantom.exit(1);
} else {
    address = system.args[1];

    page.onResourceRequested = function (req) {
        //console.log('requested: ' + JSON.stringify(req, undefined, 4));
        console.log(JSON.parse(JSON.stringify(req, undefined, 4)).url);
    };

    //page.onResourceReceived = function (res) {
    //    console.log('received: ' + JSON.stringify(res, undefined, 4));
    //};

    page.open(address, function (status) {
        if (status !== 'success') {
            console.log('FAIL to load the address');
        }
        window.setTimeout(function () {
            phantom.exit(1);
        }, 5000);
    });
}

效果:

phantomjs netlog.js http://bj.fang.ooxx.com
http://bj.fang.ooxx.com/
http://include.aifcdn.com/aifang/res/2015042706/b/Aifang_Web_Loupan_List_ListIndex.css
http://pages.aifcdn.com/prism/performance.js?v=1416480080
http://pages.aifcdn.com/js/jquery-1.9.1/jquery-1.9.1.min.js
http://pages.aifcdn.com/js/aa/bb.js
http://include.aifcdn.com/aifang/res/2015042706/b/Aifang_Web_Loupan_List_ListIndex.js
http://tracklog.ooxx.com/referrer_ooxx_pc.js
http://ifx.fang.ooxx.com/s?p=918&c=14&o=1&st=ajk
http://ifx.fang.ooxx.com/s?p=2000&c=14&r=0&sr=0&pa=&o=1&t=&st=ajk
http://pic1.ajkimg.com/display/xinfang/c43a03221d9fd83d6b409f31909ce19a/160x120.jpg
http://chart.aifcdn.com/average/price/city/?id=14&w=210&h=100&limit=6&date=20150428025144&logo=1
http://ifx.fang.ooxx.com/s?p=2001&c=14&o=1&st=ajk
  ......

另一个例子 netsniff.js 实现了将抓捕到的 网络请求导出成 HAR 格式然后可视化分析,有兴趣的同学可以参考这个官方的例子。

注意:

(1)phantomjs 的 page.settings.resourceTimeout 只能用于当前页面父请求的超时控制,并不能用于子请求的超时控制,这样当一个页面上百个请求有一个请求阻塞了,会导致整个请求卡死,好在如果它的子请求是异步的,你可以选择中断请求,获取已有的数据:

timeout 3 phantomjs netlog.js http://bj.fang.ooxx.com/|grep tracklog

(2)尽管 phantomjs 到了 2.0 已经相对成熟了,但部分文档和API功能还不完善,比如 evaluateJavaScript 的文档不完善,函数貌似还有 bug:

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

function add(arg1, arg2) {
	console.log(arg1 * arg2);
};
add(2, 3);
page.evaluateJavaScript('\
	function add(arg1, arg2) {\
		console.log(arg1 * arg2);\
	};\
	add(2, 3);\
');
phantom.exit();

//结果
6
SyntaxError: Expected token ')'

  phantomjs://webpage.evaluate():1 in evaluateJavaScript
SyntaxError: Expected token ')'

  phantomjs://webpage.evaluate():1 in evaluateJavaScript

4、Python 下的 PhantomJS:ghost.py

其实 Python 下的 ghost.py 和 PhantomJS 没有关系,这里只是对不熟悉 JS 的同学推荐下。

如果要实现第三节中的例子,ghost.py 也能做到,而且整体功能和 PhantomJS 类似:

# coding=utf-8
# 测试utf-8编码
from multiprocessing.pool import Pool
import sys

reload(sys)
sys.setdefaultencoding('utf-8')

from ghost import Ghost
import time


def requestUrl(url):
    resultStr = url + "\n"
    t1 = time.clock()
    ghost = Ghost()
    try:
        page, resources = ghost.open(url, wait=True, timeout=30)
        REQ_FOUND_FLAG = 0
        for index, trackReq in enumerate([res.url for res in resources if "tracklog" in res.url]):
            resultStr = resultStr + str(index) + "\t" + trackReq + "\n"
            REQ_FOUND_FLAG = 1
        if REQ_FOUND_FLAG == 0:
            resultStr = resultStr + "requests not found tracklog's URL: " + url + "\n"
    except Exception, e:
        resultStr = resultStr + str(e) + ": " + url + "\n"
    ghost.exit()
    t2 = time.clock()
    resultStr = resultStr + str(t2 - t1) + ": " + url + "\n"
    print resultStr + "-----------------------" + "\n"


if __name__ == "__main__":
    ts = time.time()
    urls = [
        'http://bj.ooxx.com/test/',
        'http://bj.ooxx.com/',
        'http://bj.ooxx.com/job.shtml',
        'http://bj.ooxx.com/chuzu/',
        'http://bj.ooxx.com/ershoufang/',
        'http://bj.fang.ooxx.com/?from=58_home_top',
        'http://bj.ooxx.com/ershouche/',
        'http://che.ooxx.com/',
        'http://bj.ooxx.com/sale.shtml',
        'http://bj.ooxx.com/dog/',
        'http://bj.ooxx.com/huangye/',
        'http://pic2.ooxx.com/m58/app58/m_static/home.html',
        'http://bangbang.ooxx.com/jc_pc_homepage_3.html',
        'http://jinrong.ooxx.com/k?from=58_index_ss',
        'http://about.ooxx.com/hr/'
    ]
    
    p = Pool(4)
    p.map(requestUrl, urls)
    print("cost time is: {:.2f}s".format(time.time() - ts))
    
//结果:
C:\Python27\python.exe F:/sourceDemo/test.py
http://bj.ooxx.com/test/
requests not found tracklog's URL: http://bj.ooxx.com/test/
0.585803057247: http://bj.ooxx.com/test/
-----------------------

http://bj.ooxx.com/
requests not found tracklog's URL: http://bj.ooxx.com/
0.619204294693: http://bj.ooxx.com/
-----------------------

http://bj.ooxx.com/job.shtml
0	http://tracklog.ooxx.com/referrer4.js
1	http://tracklog.ooxx.com/referrer4.js
2	http://tracklog1.ooxx.com/pc/empty.js.gif?fromid=referrer4&site_name=58&tag=pvstatall&referrer=&type=index&post_count=-1&_trackParams=NA&version=A&loadtime=376&window_size=614x454&trackURL={'new_uv':'1','new_session':'1','init_refer':'','GTID':'14301578423960.8530509551055729','cate':'9224','area':'1','pagetype':'index','GA_pageview':'/index/zhaopin/job/'}&rand_id=0.2580734915100038
1.19415236255: http://bj.ooxx.com/job.shtml
-----------------------
......

虽说 ghost.py 整个功能和 PhantomJS 类似,但它的兼容性还是要差一大截:

(1)请求没有优化,对于页面上多个相同的引用请求,ghost.py 会老老实实的请求多次,而不会只请求一次。

(2)对于 js 的异步代码和函数封装的执行,兼容性不够,无法捕获请求或执行,如下两种写法在 ghost 下都有问题:

</script><script src="//tracklog.ooxx.com/referrer_ooxx_pc.js" type="text/javascript" async defer></script>

<script type="text/javascript">
readyToDo("$",function(){
  $.getScript('http://tracklog.ooxx.com/referrer4.js');
});
</script>

(3)和 PhantomJS 一样,ghost 也存在请求超时控制不够友好的问题,但 ghost 的问题似乎更严重,不请求完成就拿不到数据。

好了,本文就介绍 PhantomJS 到这里,主要通过一个实际的例子来展示 PhantomJS 的强大功能与特性,而在实际的 web 自动化测试或者爬虫需求中,它的一些其它特性我们或许恰好就能用得上~

5、Refer:

[1] 比 Selenium 更好用的新神器 Pyppeteer

https://mp.weixin.qq.com/s/pC5rW1h_N3OSgCHlWRq2Ow

[2] can't run puppeteer in centos7

https://github.com/GoogleChrome/puppeteer/issues/391

[3] 使用pyppeteer淘宝登录

http://bit.ly/2L2OZNj

[4] centos安装使用puppeteer和headless chrome

https://segmentfault.com/a/1190000011382062

[5] Katalon + 傻瓜 == selenium

http://bit.ly/2ZuU8l5

[6] 高级爬虫: 让 Selenium 控制你的浏览器帮你爬

https://morvanzhou.github.io/tutorials/data-manipulation/scraping/5-01-selenium/

© 著作权归作者所有

大数据之路
粉丝 1605
博文 514
码字总数 333996
作品 0
武汉
架构师
私信 提问
加载中

评论(2)

x
xiatiandefeng
写的很不错,学习了42
分享一个免费好用的云端爬虫开发平台
http://www.shenjianshou.cn/
Solr_
Solr_
你好,请教个问题。运行phantomJs如果对应网页有脚本错误则不能进行evaluate了,这个如何解决?
聊聊phantomjs的优化措施

序 本文主要小结一下phantomjs的优化措施 phantomjs phantomjs相当于一个后台浏览器,有点内嵌jetty的味道,通常在自动化测试或者爬虫领域用。 优化点 池化技术,避免重复启动 对于其他语言进...

go4it
2017/11/07
73
0
Cookie欺骗工具, Python + Selenium实现!

Linux系统没有好用的cookie欺骗工具,所以决定自己写一个简单的。在这个过程中发现Selenium真是挺强大的 Cookie机制 在程序中,会话跟踪是很重要的事情。理论上,一个用户的所有请求操作都应...

Python新世界
2018/08/06
0
0
Python网络爬虫(JSON, Phantomjs, selenium/Chromedirver,豆瓣电影、斗鱼直播、京东商城爬取)

个人网站刚上线 捧捧场 谢谢~ 项目还是遇到跟多坑的 分享一下 www.baliIT.com 域名备案中 如果不能访问 可以尝试 http://106.12.86.182/ json模块 什么是json? javascript中的对象和数组 对象...

巴黎香榭
2018/11/25
0
0
使用 Node.js 实现的网页抓取

现今,网页抓取已经是一种人所共知的技术了,然而依然存在着诸多复杂性, 简单的网页爬虫依然难以胜任Ajax轮训、XMLHttpRequest,WebSockets,Flash Sockets等各种复杂技术所开发出来的现代化...

oschina
2012/12/30
16.1K
2
docker快速搭建分布式爬虫pyspider

简介 pyspider是Python中强大Web爬虫框架,并且支持分布式架构。 为什么使用docker搭建pyspider 在安装pyspider时爬过一些坑,比如使用pip install pyspider时,python的版本要求在3.6及以下...

喵来个鱼
05/01
0
0

没有更多内容

加载失败,请刷新页面

加载更多

使用CSS自定义属性构建骨架屏

写在前面 几天前看到薄荷前端团队分享的《前端骨架屏方案小结》,突然回想起一年前看到的max bock写的《Building Skeleton Screens with CSS Custom Properties》,翻译整理写下出此文,分享...

前端老手
昨天
9
0
Docker常用命令小记

除了基本的<font color="blue">docker pull</font>、<font color="blue">docker image</font>、<font color="blue">docker ps</font>,还有一些命令及参数也很重要,在此记录下来避免遗忘。 ......

程序员欣宸
昨天
9
0
MAT使用-jvm内存溢出问题分析定位

1.MAT简介: MAT 全称 Eclipse Memory Analysis Tools 是一个分析 Java堆数据的专业工具,可以计算出内存中对象的实例数量、占用空间大小、引用关系等,看看是谁阻止了垃圾收集器的回收工作,...

xiaomin0322
昨天
5
0
内网和外网之间的通信(端口映射原理)

首先解释一下“内网”与“外网”的概念: 内网:即所说的局域网,比如学校的局域网,局域网内每台计算机的IP地址在本局域网内具有互异性,是不可重复的。但两个局域网内的内网IP可以有相同的...

Jack088
昨天
6
0
3.深入jvm内核-原理、诊断与优化-4. GC算法和种类

一、GC算法和种类 GC的概念 GC算法 引用计数法 标记清除 标记压缩 复制算法 可触及性 Stop-The-World GC的对象是堆空间和永久区 引用计数法 老牌垃圾回收算法 通过引用计算来回收垃圾 使用者...

hexiaoming123
昨天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部