美文网首页让前端飞程序员
node下利用Promise更优雅的写网站爬虫

node下利用Promise更优雅的写网站爬虫

作者: 风慕李 | 来源:发表于2018-01-15 15:02 被阅读187次

上篇文章《基于node下的http小爬虫》我们简单的介绍了如何爬取菜鸟教程中node.js这一节课程的章节目录。今天我们再一次来爬爬菜鸟教程的官网,获取官网中所有大课程及大课程下的分类课程以及分类课程下的章节目录。这个工程有点大呀!只能我们一起加油咯。


开始之前呢,我们先去菜鸟教程的官网首页看看我们需要做的大概是个什么样子:

  1. 获取首页中左侧全部课程信息
  2. 获取全部课程信息中每个课程下的分类课程信息
  3. (点击分类课程进入当前课程的详细课程中),获取当前课程下的章节目录
    如果用JSON来映照这个需求大概是这个样子(这里我们举一段例子来直观感受一下):
[
    {
        title:'网站建设',
        subTitle:'网站品质',
        url:'quality/quality-tutorial.html',
        subChapter:[网站品质教程,Web 品质标准,Web 品质 - 重要的HTML元素,Web 品质样式表,Web 品质可读性,Web 无障碍(WAI),Web 品质国际化]
    }
]

明确任务需求后,就是准备工作

  1. 查看当前版本的node是否自带promise(进入一个终端命令中查看)
G:\node> node
> Promise
[Function: Promise]
>

如上终端打印可知我的node中有promise,如果你们的没有的话可以install bluebird,通过bluebird来引入,在《node中的Promisse》一文中有介绍。

2.我们还需要cheerio,这个在 《基于node下的http小爬虫》也有介绍

3.有Promise的简单概念《node中的Promisse》有简单运用介绍


接下来就直接从代码中通过注释讲解吧

var http = require('http');//获取http模块
var Promise = require('bluebird');//引入Promise模块
var cheerio = require('cheerio');//引入cheerio模块
var baseUrl = 'http://www.runoob.com';//定义node官网地址变量

var resultData = [];//{title:'',moreTitle:[{subTitle:'',url:''}]}
var data = [];//所有分类课程下章节目录

// filer 全部课程,分类课程及分类课程的链接
function filerAllChapter(html) {
    // 将爬取得HTML装载起来
    var $ = cheerio.load(html);
    // 拿到全部课程
    var courses = $('.codelist.codelist-desktop');
    //这里我希望我能获取的到的最终数据格式这个样子的,如此我们能知道主课程分类,分类课程及分类课程地址
    /**
     * {title:'',moreTitle:[{subTitle:'',url:''}]}
     */   
    var allCourseArray = [];
    courses.each(function (item) {
        // 获取主课程
        var title = $(this).find('h2').text();   

        // 分类课程
        var moreTitle = [];
        var subCourse = $(this).find('.item-top.item-1');

        subCourse.each(function (sub) {
            var subTitle = $(this).find('h4').text();
            var url = $(this).attr('href');
            moreTitle.push({
                subTitle: subTitle,
                url: url
            })
        })

        resultData.push({
            title: title,
            moreTitle: moreTitle
        })
    })

    return resultData;

}

// 分类课程下的章节目录
function filerSubChapter(html) {
    // 将爬取得HTML装载起来
    var $ = cheerio.load(html);
    // 拿到左侧边栏的每个目录
    var nodeChapter = $('#leftcolumn a');
    var chapterData = [];
    nodeChapter.each(function (item) {
        // 获取每项的地址及标题
        var title = $(this).text().trim();
        chapterData.push(title)
    })

    return chapterData;
}

// 打印过滤全课程,每个课程下的分类课程及分类课程下的目录
function getChapterData(resultData) {
    resultData.forEach(function (item) {
        console.log('\n');
        console.log("全课程 : 【 " + item.title + " 】");
        console.log('\n');
        item.moreTitle.forEach(function (item1) {
            console.log('-----------'+ item1.subTitle +'--------------');
            item1.subChapter.forEach(function (item2) {
                console.log(item2)
            })
        })
    });
}

// 利用Promise来封装get请求,promise的好处就在于比回调易维护,易理解,链式操作开发也快捷
function getPageAsync(url) {
    return new Promise(function (resolve, reject) {
        
        console.log('正在爬取 ' + url)

        http.get(url, function (res) {
            var html = '';

            // 这里将会触发data事件,不断触发不断跟新html直至完毕
            res.on('data', function (data) {
                html += data
            })

            // 当数据获取完成将会触发end事件,这里将会打印初菜鸟教程官网的html
            res.on('end', function () {
                // 这里是promise的回调函数,我们通过resolve将获取的html传递下去
                resolve(html)
            })
        }).on('error', function (e) {
            // 如果爬取出错,则通过reject返回
            reject(e)
            console.log('获取node官网相关数据出错')
        })
    })
}

// 获取分类课程的链接地址
var allCourseArray = [];
var urls = [];
getPageAsync(baseUrl).then(function (pages) {
    // pages及为菜鸟教程首页的HTML

    // {title:'',moreTitle:[{subTitle:'',url:''}]}
    var courses = filerAllChapter(pages);

    // 将分类课程的地址取出来
    courses.forEach(function (item) {
        item.moreTitle.forEach(function (item1) {
            urls.push(item1.url.split('//www.runoob.com')[1]);
        })
    })

    // 访问每个分类课程,将每个分类课程的信息放入allCourseArray数组
    urls.forEach(function (path) {
        allCourseArray.push(getPageAsync(baseUrl + path))
    })

}).then(function(){
    // 并发处理每个分类课程
    Promise.all(allCourseArray)
        .then(function (pages) {
            // pages所有分类课程下的页面html
            pages.forEach(function (html) {
                // 获取分类课程下章节目录
                data.push(filerSubChapter(html))
            })

        })
        .then(function () {
            for (var i = 0; i < resultData.length; i++) {
                if(i>=1){
                    data.splice(0,resultData[i-1].moreTitle.length);
                }
                for (var j = 0; j < resultData[i].moreTitle.length; j++) {
                    resultData[i].moreTitle[j]['subChapter'] = data[j]
                }
            }
           getChapterData(resultData)
        })

})

终端打印结果如下:(因为信息较多这里只显示一小部分)

G:\node> node node-http/node-http-promise.js
正在爬取 http://www.runoob.com
正在爬取 http://www.runoob.com/html/html-tutorial.html
正在爬取 http://www.runoob.com/html/html5-intro.html
正在爬取 http://www.runoob.com/css/css-tutorial.html
正在爬取 http://www.runoob.com/css3/css3-tutorial.html
正在爬取 http://www.runoob.com/bootstrap/bootstrap-tutorial.html
正在爬取 http://www.runoob.com/bootstrap4/bootstrap4-tutorial.html
正在爬取 http://www.runoob.com/font-awesome/fontawesome-tutorial.html
正在爬取 http://www.runoob.com/foundation/foundation-tutorial.html
正在爬取 http://www.runoob.com/js/js-tutorial.html
正在爬取 http://www.runoob.com/htmldom/htmldom-tutorial.html
正在爬取 http://www.runoob.com/jquery/jquery-tutorial.html
正在爬取 http://www.runoob.com/angularjs/angularjs-tutorial.html
正在爬取 http://www.runoob.com/angularjs2/angularjs2-tutorial.html
正在爬取 http://www.runoob.com/vue2/vue-tutorial.html
正在爬取 http://www.runoob.com/react/react-tutorial.html
正在爬取 http://www.runoob.com/jqueryui/jqueryui-tutorial.html


全课程 : 【  HTML / CSS 】


-----------【学习 HTML】--------------
HTML 教程
HTML 简介
HTML 编辑器
HTML 基础
HTML 元素
HTML 属性
HTML 标题
HTML 段落
HTML 文本格式化
HTML 链接
HTML 头部

。。。。。。。。。。。。。

如此我们就将菜鸟教程官网的全部课程,分类课程及分类课程下的章节目录都对应打印出来了。爬虫可以做很多有趣的事情,大家不妨去试一试,关于爬虫我就介绍到这里。

原文地址 https://gitee.com/wangFengJ/node/blob/master/node-http/node-http-promise.js

相关文章

网友评论

    本文标题:node下利用Promise更优雅的写网站爬虫

    本文链接:https://www.haomeiwen.com/subject/kiaroxtx.html