Node爬取网站信息-慕课网
鹿酒 2017/3/14 Node.js
之前一直在学习如何使用NodeJS,听的一知半解,知道一些API,但不明白到底怎么用。后面,老师讲了一个小案例,爬取慕课网的部分信息,让我有了直观的认识。
第一遍跟着老师敲了一遍,但是错误百出:

实际上是因为我在声明Promise时,没有写对形式,导致promise对象设置错误。
还有一些小错误,但是能根据报错信息改正过来。比如变量名称错误,Not Function,等等。
下面是最后成功爬取的代码:
//加载内置模块http的方法
var http = require('http');
//通过bluebird加载Promise对象
var Promise = require('bluebird');
//加载cheerio API
var cheerio = require('cheerio');
//为了简写,把重复的url部分放在baseUrl里,用的时候遍历一下就可以了
var baseUrl = 'http://www.imooc.com/learn/';
var videoIds = [75,134,197,259,348,637];
function filterChapters(html){
var $ = cheerio.load(html);
//trim()消除空格
var chapters = $('.chapter')
var title = $('.course-infos .w .path span').text();
var number = parseInt($($('.static-item .js-learn-num')).text().trim(), 10)
/* 预想的数据结构
courseData = {
title: title, 课程名称
number: number, 学习人数
videos: [{
chapterTitle: '',
videos:[
title: '',
id: ''
]
}]
}
*/
var courseData = {
title: title,
number: number,
videos: []
};
chapters.each(function(item){
var chapter = $(this);
var chapterTitle = chapter.find('strong').text();
var videos = chapter.find('.video').children('li')
var chapterData = {
chapterTitle: chapterTitle,
videos: []
}
videos.each(function(item){
var video = $(this).find('.J-media-item');
var videoTitle = video.text();
var id = video.attr('href').split('/video/')[1];
chapterData.videos.push({
title: videoTitle,
id: id
})
})
courseData.videos.push(chapterData);
})
return courseData;
}
function printCourseInfo(coursesData) {
coursesData.forEach(function(courseData) {
console.log(courseData.number + '人学过' + courseData.title);
})
coursesData.forEach(function(courseData) {
console.log('###' + courseData.title);
courseData.videos.forEach(function(item) {
var chapterTitle = item.chapterTitle;
console.log(chapterTitle);
item.videos.forEach(function(video) {
console.log(' 【'+video.id+'】'+video.title);
})
})
})
console.log('\n');
}
/*
异步的爬取数据,传入需要爬的网站的url
*/
function getPageAsync(url) {
return new Promise(function(resolve,reject) {
console.log('正在爬取'+url);
//给http模块添加get方法
//get方法必须传入url,可以添加success方法(成功时运行的函数)
http.get(url,function(res) {
var html = '';
//on()绑定事件
//data事件是不断获取返回的数据
res.on('data',function(data){
//获取所有页面代码
html += data;
})
//end事件是结束发送请求
res.on('end',function(){
//undefined
resolve(html);
})
}).on('error',function(e){
//绑定错误事件,返回错误原因
reject(e);
console.log('获取课程数据出错');
})
})
}
//Promise对象的数组
var fetctCourseArray = [];
//遍历所有的课程id(id保存在这个数组videoIds里了)
videoIds.forEach(function(id) {
//爬到所有课程页面的源码,返回Promise对象,并保存在数组里
fetctCourseArray.push(getPageAsync(baseUrl + id));
})
//Promise.all(),返回一个Promise对象,使传入的参数(可迭代参数,比如数组)都resolve一遍
//Promise.resolve(),解析promise对象,按照后面的.then方法解析
//.then(),链式调用
Promise
.all(fetctCourseArray)
.then(function(pages) {
//存放页面解析结果
var coursesData = [];
//pages是课程页面
pages.forEach(function(html) {
//通过filterChapters解析html
var courses = filterChapters(html);
//把解析的结果放进coursesData[]
coursesData.push(courses);
})
//由高到低排序
coursesData.sort(function(a,b) {
return a.number > b.number
})
//打印输出
printCourseInfo(coursesData);
})
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
重点的部分写了注释。
还有个问题解决不了:获取不到学习人数,听别人讲,是因为慕课网使用JS显示人数,所以爬虫获取不到信息。