项目初始化
创建一个package.json文件,webstorm快捷创建package.json非常简单。
使用 npm init 快速创建。
工具模块
需要下载的的模块
- superagent 页面数据下载
- cheerio 页面数据解析
这是2个npm包,我们先下载:
npm install superagent cheerio --save
需要引入的模块
- fs
- path
引入项目依赖
const cheerio = require('cheerio');
const agent = require('superagent');
const path = require('path');
const fs = require('fs');
定义地址数组
我们希望以队列的形式逐个对这些地址进行访问,获取HTML代码,以便后续处理:
const urls = [{
page:1,
url:"https://www.imooc.com/course/list?c=fe&page=1"
},{
page:2,
url:"https://www.imooc.com/course/list?c=fe&page=2"
},{
page:3,
url:"https://www.imooc.com/course/list?c=fe&page=3"
}];
定义数据结构
慕课网课程列表:
对此我们定义如下的数据结构:
[
{
page: 1,
data: [
{
title:"", // 课程标题
imgurl:"", // 课程图片
level:"", // 等级
studynum:0, // 学习人数
description:"xxxx" // 课程描述
}
...... // 每一个页面有多条课程信息
]
}
...... // 一共有多个页面
]
superagent 页面数据下载
superagent是nodejs里一个非常方便的客户端请求代码模块,superagent是一个轻量级的,渐进式的ajax API,可读性好,学习曲线低,内部依赖nodejs原生的请求API,适用于nodejs环境下。
基本使用方法:具体的请自行点击连接查看哟...
request
.get('/login')
.end(function(err, res){
// code
});
cheerio 页面数据解析
cheerio是一个node的库,可以理解为一个Node.js版本的jquery,用来从网页中以 css selector取数据,使用方式和jquery基本相同。
需要先loading一个需要加载html文档,后面就可以jQuery一样使用操作页面了。
基本使用方法:具体的请自行点击连接查看哟...
const cheerio = require('cheerio');
const $ = cheerio.load('<ul id="fruits">...</ul>');
$('#fruits').addClass('newClass');
使用Promise实现队列
这才是本篇文章的重头戏...
- 我们需要使用数组的一个方法 reduce()
arr.reduce([callback, initialValue])
有不太懂这个方法的可以查看我写的笔记:https://segmentfault.com/n/13...
reduce() 方法接收一个函数作为累加器(accumulator),数组中的每个值(从左到右)开始缩减,最终为一个值。
callback (执行数组中每个值的函数,包含四个参数)
initialValue (作为第一次调用 callback 的第一个参数。)
- 还有一个是Promise实现异步处理
有不太懂这个方法的可以查看我写的笔记:https://segmentfault.com/n/13...
具体是使用Promise的这个方法:
Promise.resolve()
这个方法返回一个fulfilled的Promise实例,或者原始的Promise实例。
代码实现:
// 实现队列
// 本质: 对.then()方法实现累加
let curPromise = urls.reduce((promise,curl) => {
return promise.then(() => {
return new Promise(resolve => {
// 网络获取当前地址的网页内容
requestGet(curl,() => {
resolve();
});
});
});
},Promise.resolve());
将数据写入result.json文件中
代码实现:
// 写入数据
curPromise.then(()=>{
fs.writeFile('result.json', JSON.stringify(result), function (err) {
if(err) throw new Error("appendFile failed...");
console.log("数据写入success...");
});
});
完整代码
// 项目依赖
const cheerio = require('cheerio');
const agent = require('superagent');
const path = require('path');
const fs = require('fs');
// 地址数据
const urls = [{
page:1,
url:"https://www.imooc.com/course/list?c=fe&page=1"
},{
page:2,
url:"https://www.imooc.com/course/list?c=fe&page=2"
},{
page:3,
url:"https://www.imooc.com/course/list?c=fe&page=3"
}];
// 最终的数据
let result = [];
// 数据结构
/**
* [
* {
* page: 1,
* data: [
* {title:xx,imgurl:xx...},
* ......
* ]
* }
* ......
* ]
*/
// 发起get请求
function requestGet(urlObj,callback){
agent.get(urlObj.url)
.end((err,res) => {
if(err) throw new Error(err);
// 分析页面
let pageJson = analysis(res.text);
// 拼接数据
result.push({
page:urlObj.page,
data:pageJson
});
console.log(`写入第${urlObj.page}页的数据...`);
// 执行回调
callback();
});
}
// 对网页分析
function analysis(data){
let page = [];
let $ = cheerio.load(data);
let courseArr = $(".course-list").find(".course-card-container");
courseArr.each((index,element) => {
let _this = $(element);
// 组装数据
page.push({
title:_this.find(".course-card-name").text(),
imgurl:path.join("http:",_this.find(".course-card-top img").attr("src")),
level:_this.find(".course-card-info span:first-child").text(),
// level:_this.find(".icon-set_sns").parent().prev().text(),
studynum:_this.find(".icon-set_sns").parent().text(),
description:_this.find(".course-card-desc").text()
});
});
return page;
}
// 实现队列
// 本质: 对.then()方法实现累加
let curPromise = urls.reduce((promise,curl) => {
return promise.then(() => {
return new Promise(resolve => {
// 具体的内容
requestGet(curl,() => {
resolve();
});
});
});
},Promise.resolve());
// 写入数据
curPromise.then(()=>{
fs.writeFile('result.json', JSON.stringify(result), function (err) {
if(err) throw new Error("appendFile failed...");
console.log("数据写入success...");
});
});
启动项目
node app.js
可以看到终端有次序的输出了以下内容:
当打开生成的 result.json 文件,其结构也符合我们的预期:
至此,这篇文章也就结束啦,如果您有好的想法请留言哟。
持续学习中...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。