作为比较好的动态网页爬虫手段,phantomjs在许多方面令人比较满意。调用Phantomjs的方式,一般有如下几种情况。

1. 命令行模式

  在CMD或Shell中,直接输入phantomjs回车,进入命令行模式,能够完成各种操作。但一般情况是通过命令用调用phantomjs来完成爬虫或模拟工作,具体的代码放在JS中。如

../bin/phantomjs --debug=yes ./server.js 8910
../bin/phantomjs --debug=yes ./hello.js

其中phantomjs参数直接放在phantomjs后面,脚本作为参数放在其次,最后添加脚本的参数列表。

2. selenium调用

  selenium是一套完整的测试爬虫工具,能够调用IE、Chrome、Firefox等浏览器内核API完成相应的功能,也可以调用如Phantomjs、HtmlUnitDriver等模拟浏览器作为调用接口。

  调用浏览器内核与不调用浏览器内核的浏览器[这里指的是HtmlUnitDriver],浏览器内核的API,可以完成截图功能及其他浏览器功能,但HtmlunitDriver采用的是JS模拟浏览器的策略,因此不具备截图等浏览器功能。

2.1. Java+selenium+Phantomjs调用

public static PhantomJSDriver getPhantomJspublic static PhantomJSDriver getPhantomJs(String phantomJS,
		String userAgent, boolean loadImages, boolean jsEnabled,
		String encoding,boolean proxyEnabled,String... proxys) {

	System.setProperty("phantomjs.binary.path", phantomJS);
	DesiredCapabilities desiredCapabilities = DesiredCapabilities
			.phantomjs();
	if (userAgent != null) {
		desiredCapabilities.setCapability(
				"phantomjs.page.settings.userAgent", userAgent);
		desiredCapabilities.setCapability(
				"phantomjs.page.customHeaders.User-Agent", userAgent);
	}
	desiredCapabilities.setJavascriptEnabled(jsEnabled);
	PhantomJSDriver driver = null;
	List cli = new ArrayList<>();
	cli.add("--load-images=" + loadImages);
	cli.add("--output-encoding=" + encoding);
	
	if (proxyEnabled) {
		String proxy_ip = proxys[0];
		String proxy_port = proxys[1];
		String proxy_user = proxys[2];
		String proxy_pass = proxys[3];
		cli.add("--proxy="+proxy_ip+":"+ proxy_port));
		cli.add("--proxy-auth=" +proxy_user+":" +proxy_pass);
	}
	desiredCapabilities.setCapability(
			PhantomJSDriverService.PHANTOMJS_CLI_ARGS, cli);
	driver = new PhantomJSDriver(desiredCapabilities);
	driver.manage().timeouts()
			.implicitlyWait(WAIT_TIME, TimeUnit.MILLISECONDS);
	driver.manage().deleteAllCookies();

	return driver;
}

2.2. Java+selenium+HtmlunitDriver调用

public static HtmlUnitDriver getHtmlUnitDriverpublic static HtmlUnitDriver getHtmlUnitDriver(String userAgent,
		boolean jsEnabled, boolean loadImages, boolean proxyEnabled,
		String... proxys) {

	DesiredCapabilities desiredCapabilities = DesiredCapabilities
			.htmlUnit();
	desiredCapabilities.setCapability("phantomjs.page.settings.loadImages",
			false);
	desiredCapabilities.setJavascriptEnabled(jsEnabled);
	if (userAgent != null) {
		desiredCapabilities.setCapability(
				"phantomjs.page.settings.userAgent", userAgent);
		desiredCapabilities.setCapability(
				"phantomjs.page.customHeaders.User-Agent", userAgent);
	}
	desiredCapabilities.setCapability(
			PhantomJSDriverService.PHANTOMJS_CLI_ARGS,
			new String[] { "--load-images=" + loadImages });
	HtmlUnitDriver driver = null;
	if (proxyEnabled) {

		String proxy_ip = proxys[0];
		String proxy_port = proxys[1];
		String proxy_user = proxys[2];
		String proxy_pass = proxys[3];

		Proxy proxy = new Proxy();
		proxy.setHttpProxy(proxy_ip + ":" + proxy_port);
		desiredCapabilities.setCapability(CapabilityType.PROXY, proxy);
		driver = new HtmlUnitDriver(desiredCapabilities) {
			@Override
			protected WebClient modifyWebClient(WebClient client) {
				DefaultCredentialsProvider creds = new DefaultCredentialsProvider();
				creds.addCredentials(proxy_user, proxy_pass);
				client.setCredentialsProvider(creds);
				return client;
			}
		};
	} else
		driver = new HtmlUnitDriver(desiredCapabilities);
	driver.manage().timeouts()
			.implicitlyWait(10 * 1000, TimeUnit.MILLISECONDS);
	driver.manage().deleteAllCookies();

	return driver;
}

3. 服务器模式

  phantomjs服务器模式,需要在服务端开启服务,然后在客户端发起Http请求然后返回想要的内容,就是一个C/S模式的服务。

3.1. 服务端

server.js内容如下所示

var webserver = require('webserver').create();
var page = require('webpage').create();
var system = require('system');
var port = system.args[1];
webserver.listen(system.args[1], function(request, response) {
    var url = request.headers.url;// conf target url in headers
    page.open(url, function(status) {
        var title = page.evaluate(function() {
            return $(":root").html();// return pageSource
        });
        response.write(title);
        response.close();
    });
});

命令行启动

bin/phantomjs example/server.js 8910

3.2. 客户端

  在客户端发起http请求,无论是get,还是post只要能够获取到Headers里面的目标URL,就可以返回pageSource。当然,这里最好使用异步加载网页内容。

Map<String, String> params = Map<String, String> params = new HashMap();
params.put("url", targetURL);

public static String HttpPost(String url, String postDataStr)
		throws MalformedURLException, IOException {
		
	URLConnection conn = new URL(url).openConnection();
	conn.addRequestProperty("method", "post");
	conn.addRequestProperty("ContentType",
			"application/x-www-form-urlencoded");
	conn.addRequestProperty("method", "post");
	conn.addRequestProperty("http.socket.timeout", "60000");
	conn.setDoOutput(true);
	conn.getOutputStream().write(
			URLEncoder.encode(postDataStr, "utf-8").getBytes());
			
	StringBuilder sb = new StringBuilder();
	BufferedReader br = new BufferedReader(new InputStreamReader(
			conn.getInputStream()));
	String line = null;
	while ((line = br.readLine()) != null)
		sb.append(line);
		
	return sb.toString();
}

4. 比较

方式 优点 缺点
命令行模式 1、调用简单 1、需要配合JS
2、无法大规模并发控制
服务器模式 1、减少内存
2、客户端方便
1、存在并发限制,最多10个线程
2、试验产品,可能存在安全隐患
selenium调用 1、客户端精准控制
2、调用比较方面,无需写JS
3、可大规模并发
1、“吃”内存:内存随线程数增加较快
2、需手动释放内存,确保phantomjs释放

5. 注

  1. 由于目前phantomjs已停止更新维护,所以可以选择其他带有webkit内核的模拟浏览器,如Headless Chrome等作为测试/爬虫的首选。
  2. phantomjs使用代理,最好使用无密码代理。

6. 参考

  1. Phantomjs正确打开方式
  2. Phantomjs 进程通信方式