实例:使用 cheerio 抓取网页数据,并通过 ejs 模板输出静态页面

这篇文章中,我将使用 cheerio 从 http://ss.uprogrammer.cn 上抓取到免费的 shadowsocks 账号,然后通过 ejs 模板,输出成新的网页。

cheerio 是 nodejs 的抓取页面模块,为服务器特别定制的,快速、灵活、实施的 jquery 核心实现,适合各种 Web 爬虫程序。详见: https://github.com/cheeriojs/cheerio

ejs 是一个 JavaScript 模板库,用来从 JSON 数据中生成 HTML 字符串。详见: https://github.com/tj/ejs

下面的截图中显示,http://ss.upgrogrammer.cn 上有一个免费 shadowsocks 账号的展示区域。

http://ss.uprogrammer.cn 科学上网,免费shadowsocks账号

并且通过 chrome 的审查元素,可以看到相关的源码。

http://ss.uprogrammer.cn 科学上网,免费shadowsocks账号

那么,接下来,我们就要抓取这部分内容中的免费 shadowsocks 账号信息。

新建 nodejs 项目,并安装相关依赖

1
2
3
npm init
...
npm install cheerio ejs --save

以下图片显示了通过 npm init 命令创建 nodejs 项目的过程:

http://ss.uprogrammer.cn 科学上网,免费shadowsocks账号

获取 http://ss.upgrammer.cn 完整网页内容

在使用 cheerio 来抓取我们所需的内容之前,我们要通过 http 模块,将完整页面先获取到。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var http = require("http");

function download(url, callback) {
http.get(url, function(res) {
var data = "";
res.on('data', function(chunk) {
data += chunk;
});
res.on('end', function() {
callback(data);
})
}).on("err", function(e) {
console.log(e);
callback(null);
});
}

以上的代码片段通过 GET 方法获取对应 url 的数据,并通过 callback 传回。

从获取到的完整页面中抓取免费账号信息

通过在浏览器中查看源代码(具体内容其实也就是我们通过上面的 download 函数返回的结构),我们发现,这个页面结构是非常简单的。

  1. 所有的免费账号信息都在 id 为 account_container<div> 标签中。
  2. 所有的单条账号信息,都在 <div class="col-md-4 text-center">...</div> 中。
  3. 单条账号中的信息,是连续的6个<h4>标签,按照服务器地址、端口号、密码、加密方式、状态、说明的方式存在。
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
download("http://ss.uprogrammer.cn/", function(data) {
if (!data) {
console.log('no data found');
return;
}

var accounts = [];

$ = cheerio.load(data);

// 通过 cheerio 的选择器获取免费账号信息单元
var ss = $('#account_container > .col-md-4');

for (var i = 0; i < ss.length; i++) {
var s = {};

// 解析服务器
var server = $(ss[i]).children().first();
s['server'] = server.text().substr(7);
console.log(s['server']);

// 解析端口
var port = $(server).next();
s['port'] = port.text().substr(3);
console.log(s['port']);

// 解析密码
var password = $(port).next();
s['password'] = password.text().substr(4);
console.log(s['password']);

// 解析加密方式
var encrypt = $(password).next();
s['encrypt'] = encrypt.text().substr(5);
console.log(s['encrypt']);

// 解析状态
var status = $(encrypt).next();
s['status'] = status.text().substr(3);
console.log(s['status']);

// 解析说明
var tip = $(status).next();
s['tip'] = tip.text();
console.log(s['tip']);

account.push(s);
console.log("===");
}
});

主要的代码片段如上,最终 account 变量中将保存所有的账号信息。

将数据通过 ejs 模板渲染成新的页面

我们需要定义一个模板文件 template.ejs,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>实例:使用 cheerio 抓取网页数据,并通过 ejs 模板输出静态页面</title>
</head>
<body>
<div id="content">
<% servers.forEach(function(server){ %>
<div>
<h4>服务器地址:<%= server.server %></h4>
<h4>端口:<%= server.port %></h4>
<h4>密码:<%= server.password %></h4>
<h4>加密方式:<%= server.encrypt %></h4>
<h4>状态:<%= server.status %></h4>
<h4><%= server.tip %></h4>
</div>
<% }) %>
</div>
</body>
</html>

这是一个非常简单的模板文件,只是说明传递数据与模板解析的功能。其中将接收名为 servers 的参数。

下面我们定义一个 render 函数,读取 template.ejs 模板文件,并根据传入的 servers 参数进行渲染,输出到 index.html 中。

1
2
3
4
5
6
7
8
9
10
11
function render(accounts) {
fs.readFile('./template.ejs', function (e, v) {
var ret = v.toString();

var options = {servers: accounts}
var template = ejs.render(ret, options);
fs.writeFile('./index.html', template, function (err) {
if (err) throw err;
});
});
}

OK。完成上面的过程,整个程序结构如下:

http://ss.uprogrammer.cn 科学上网,免费shadowsocks账号

最后,在项目根目录中执行 node index.js, 正常在终端中可以看到日志输出,并生成 index.html 文件。通过浏览器打开 index.html 就可以验收成果啦。

完整的源码见:https://github.com/linfuyan/demo-cheerio-ejs