分享

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

 bylele 2019-01-23

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

-HtmlUnit

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

- Selenium

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

- PhantomJs

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

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

本地

需要构造目标执行的js文件,利用命令行调用PhantomJS
示例:
window平台下
PhantomJs.exe target.js param1

对应的本地target.js可参考如下示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
<code><code><code>"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();
            }
        });</some></code></code></code>

在java程序中,通过调用控制台执行命令

1
2
3
4
5
6
7
8
9
10
<code><code><code>        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();</code></code></code>

搭建远程服务器

保证远程服务器指定端口开启
示例:
在阿里ecs上开启指定端口,如3003
打开控制台,在安全组中添加自定义TCP连接,可访问的ip组设置为0.0.0.0/0,同时配置入网和出网端口

操作步骤

1) 官网下载exe文件至指定位置(linux平台同理)
2) 新建一个server.js文件
3) 命令行运行PhantomJS server.js即可开启服务
4) 本地通过在浏览器或者java代码中提交http请求,即可获得响应,url为 https://远程服务器ip地址:端口号/https://自定义url

此处server.js为关键,其设置了服务器的监听端口及响应请求逻辑
server.js示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<code><code><code>"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("<title></title>");
        // note: writeBody can be called multiple times
       // var url = "https://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
    }
}</some></code></code></code>

提供本地发起请求Java代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<code><code><code>        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();</code></code></code>

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多