现如今的爬虫再也不是简单的爬取静态页面,解析Html文本这么简单,许多单页面应用,异步请求调用,页面初始化js渲染等技术的使用,使得传统的通过发起http请求获得的Document无法直接使用。因此,基于实际业务需求,在爬取某电商平台数据时,发现其页面特定位置为js渲染,固此,由此一文,基于实际代码测试,分析HtmlUnit/Selenium/PhantomJs三类流行的js渲染引擎。
1) 内置Rhinojs浏览器引擎,没有哪一款浏览器使用该内核
2) 解析速度一般
3) 解析JS/CSS差
4) 无浏览器界面
1) Seleninum 1+ WebDriver = Selenium
2) 基于本地安装的浏览器,需打开浏览器
3) 需要引用相应的WebDriver,正确配置webdriver的路径参数
4) 在爬取大量js渲染页面时明显不合适
1) 神器,短小精悍
2) 可本地化运行,也可作为服务端运行
3) 基于webkit内核,性能及表现良好
4) 完美解析绝大部分页面
需要构造目标执行的js文件,利用命令行调用PhantomJS
示例:
window平台下
PhantomJs.exe target.js param1
对应的本地target.js可参考如下示例:
"use strict";
var page = require('webpage').create();
var system = require('system');
if (system.args.length !== 2) {
console.log('Usage: server.js ' );
phantom.exit(1);
} else {
var url = system.args[1];
page.open(url, function (status) {
console.log(page.content);
phantom.exit();
}
});
在java程序中,通过调用控制台执行命令
Runtime runtime = Runtime.getRuntime();
Process p = runtime.exec("D:/phantomjs.exe target.js url);
InputStream is = p.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
StringBuffer sb = new StringBuffer();
String tmp = "";
while((tmp = br.readLine())!=null){
sb.append(tmp);
}
return sb.toString();
保证远程服务器指定端口开启
示例:
在阿里ecs上开启指定端口,如3003
打开控制台,在安全组中添加自定义TCP连接,可访问的ip组设置为0.0.0.0/0,同时配置入网和出网端口
1) 官网下载exe文件至指定位置(linux平台同理)
2) 新建一个server.js文件
3) 命令行运行PhantomJS server.js即可开启服务
4) 本地通过在浏览器或者java代码中提交http请求,即可获得响应,url为 http://远程服务器ip地址:端口号/http://自定义url
此处server.js为关键,其设置了服务器的监听端口及响应请求逻辑
server.js示例代码:
"use strict";
var page = require('webpage').create();
var server = require('webserver').create();
var system = require('system');
var host, port;
if (system.args.length !== 2) {
console.log('Usage: server.js ' );
phantom.exit(1);
} else {
port = system.args[1];
var listening = server.listen(port, function (request, response) {
console.log("GOT HTTP REQUEST");
console.log(JSON.stringify(request, null, 4));
// we set the headers here
response.statusCode = 200;
response.headers = {"Cache": "no-cache", "Content-Type": "text/html"};
// this is also possible:
response.setHeader("databee", "databee");
// now we write the body
// note: the headers above will now be sent implictly
//response.write("YES! ");
// note: writeBody can be called multiple times
// var url = "http://www.baidu.com";
var url = request.url;
url = url.substring(1);//获得的url较为奇怪,根据request的内容进行url改造成合规url
page.open(url, function (status) {
if (status !== 'success') {
response.statusCode = 403;
response.headers = {
'Cache': 'no-cache',
'Content-Type': 'text/html'
};
response.write("FAIL");
response.close();
console.log('FAIL to load the address');
} else {
response.statusCode = 200;
response.headers = {
'Cache': 'no-cache',
'Content-Type': 'text/html'
};
//console.log(page.content)
response.write(page.content);
response.close();//response.close()表明响应结束,必须加入
console.log('Send success');
}
});
//response.close();
});
if (!listening) {
console.log("could not create web server listening on port " + port);
phantom.exit();//代表退出phantom
}
}
提供本地发起请求Java代码示例
URL url = new URL(finalUrl);//finalUrl此时为get请求url
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
InputStream is = null;
BufferedReader br = null;
if (conn.getResponseCode() == 200) {
is = conn.getInputStream();
} else {
is = conn.getErrorStream();
}
br = new BufferedReader(new InputStreamReader(is));
String line = "";
StringBuilder sb = new StringBuilder();
while ((line = br.readLine()) != null) {
sb.append(line);
}
return sb.toString();
先了解,后使用 在未深入了解三款js渲染引擎时,走了许多弯路,尤其是在HtmlUnit的使用上,发现经常性报内存溢出,js/css渲染错误等各种问题
先官网,后博客 很多问题往往在官网上能够得到解答,尤其是官网上提供了响应的demo或者example说明,仔细阅读提供的示例代码,及时在没有注释说明的前提下,通过阅读代码,也能知其大致用法
先业务,后工具 工具往往只是解决问题的一个手段和方法,如果对自身业务不了解,盲目使用工具,势必得不偿失。许多工具有其自身特点,如何正确使用,往往取决于自身的业务场景于需求。因此,凡是无绝对,没有最好,只有更好;没有最差,只有更差。