JS渲染引擎比较HtmlUnit/Selenium/PhantomJs

来源:互联网 发布:mac pr防抖 编辑:程序博客网 时间:2024/06/18 09:24

JS渲染引擎比较HtmlUnit/Selenium/PhantomJs

现如今的爬虫再也不是简单的爬取静态页面,解析Html文本这么简单,许多单页面应用,异步请求调用,页面初始化js渲染等技术的使用,使得传统的通过发起http请求获得的Document无法直接使用。因此,基于实际业务需求,在爬取某电商平台数据时,发现其页面特定位置为js渲染,固此,由此一文,基于实际代码测试,分析HtmlUnit/Selenium/PhantomJs三类流行的js渲染引擎。

-HtmlUnit

1) 内置Rhinojs浏览器引擎,没有哪一款浏览器使用该内核2) 解析速度一般3) 解析JS/CSS差4) 无浏览器界面

- Selenium

1) Seleninum 1+ WebDriver = Selenium2) 基于本地安装的浏览器,需打开浏览器3) 需要引用相应的WebDriver,正确配置webdriver的路径参数4) 在爬取大量js渲染页面时明显不合适

- PhantomJs

1) 神器,短小精悍2) 可本地化运行,也可作为服务端运行3) 基于webkit内核,性能及表现良好4) 完美解析绝大部分页面

基于实测结果,在爬取大量任务时,推荐将PhantomJs作为服务端使用,此处,分别介绍本地及远程服务端使用例子(也可查看官网example)

本地

需要构造目标执行的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 <some port>');    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 <some port>');    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("<html><head><title>YES!</title></head>");        // 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说明,仔细阅读提供的示例代码,及时在没有注释说明的前提下,通过阅读代码,也能知其大致用法

先业务,后工具 工具往往只是解决问题的一个手段和方法,如果对自身业务不了解,盲目使用工具,势必得不偿失。许多工具有其自身特点,如何正确使用,往往取决于自身的业务场景于需求。因此,凡是无绝对,没有最好,只有更好;没有最差,只有更差。

原创粉丝点击