Node.js爬虫-爬取慕课网课程信息

Posted 老板丶鱼丸粗面

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Node.js爬虫-爬取慕课网课程信息相关的知识,希望对你有一定的参考价值。

第一次学习Node.js爬虫,所以这时一个简单的爬虫,Node.js的好处就是可以并发的执行

这个爬虫主要就是获取慕课网的课程信息,并把获得的信息存储到一个文件中,其中要用到cheerio库,它可以让我们方便的操作html,就像是用jQ一样

开始前,记得

npm install cheerio

 

为了能够并发的进行爬取,用到了Promise对象

//接受一个url爬取整个网页,返回一个Promise对象
function getPageAsync(url){
    return new Promise((resolve,reject)=>{
        console.log(`正在爬取${url}的内容`);
        http.get(url,function(res){
            let html = \'\';

            res.on(\'data\',function(data){
                html += data;
            });

            res.on(\'end\',function(){
                resolve(html);
            });

            res.on(\'error\',function(err){
                reject(err);
                console.log(\'错误信息:\' + err);
            })
        });
    })
}

 

在慕课网中,每个课程都有一个ID,我们事先要把想要获取课程的ID写到一个数组中,而且每个课程的地址都是一个相同的地址加上ID,所以我们只要把地址和ID拼接起来就是课程的地址

const baseUrl = \'http://www.imooc.com/learn/\';
const baseNuUrl = \'http://www.imooc.com/course/AjaxCourseMembers?ids=\';
//获取课程的ID
const videosId = [773,371];

 

为了使获取每个课程内容时并发执行,要使用Promise中的all方法

Promise
    //当所有网页的内容爬取完毕
    .all(courseArray)
    .then((pages)=>{
        //所有页面需要的内容
        let courseData = [];

        //遍历每个网页提取出所需要的内容
        pages.forEach((html)=>{
            let courses = filterChapter(html);
            courseData.push(courses);
        });

        //给每个courseMenners.number赋值
        for(let i=0;i<videosId.length;i++){
            for(let j=0;j<videosId.length;j++){
                if(courseMembers[i].id +\'\' == videosId[j]){
                    courseData[j].number = courseMembers[i].numbers;
                }
            }
        }

        //对所需要的内容进行排序
        courseData.sort((a,b)=>{
            return a.number > b.number;
        });

        //在重新将爬取内容写入文件中前,清空文件
        fs.writeFileSync(outputFile,\'###爬取慕课网课程信息###\',(err)=>{
            if(err){
                console.log(err)
            }
        });
        printfData(courseData);
    });

 

在then方法中,pages是每个课程的HTML页面,我们还得从中提取出我们需要的信息,需要使用下面的函数

//接受一个爬取下来的网页内容,查找网页中需要的信息
function filterChapter(html){
    const $ = cheerio.load(html);

    //所有章
    const chapters = $(\'.chapter\');


    //课程的标题和学习人数
    let title = $(\'.hd>h2\').text();
    let number = 0;

    //最后返回的数据
    //每个网页需要的内容的结构
    let courseData = {
        \'title\':title,
        \'number\':number,
        \'videos\':[]
    };

    chapters.each(function(item){
        let chapter = $(this);
        //文章标题
        let chapterTitle = Trim(chapter.find(\'strong\').text(),\'g\');

        //每个章节的结构
        let chapterdata = {
            \'chapterTitle\':chapterTitle,
            \'video\':[]
        };


        //一个网页中的所有视频
        let videos = chapter.find(\'.video\').children(\'li\');
        videos.each(function(item){
            //视频标题
            let videoTitle = Trim($(this).find(\'a.J-media-item\').text(),\'g\');
            //视频ID
            let id = $(this).find(\'a\').attr(\'href\').split(\'video/\')[1];
            chapterdata.video.push({
                \'title\':videoTitle,
                \'id\':id
            })
        });

        courseData.videos.push(chapterdata);

    });

    return courseData;
}

注意:在上面中将课程的学习人数设置为了0是因为学习课程人数是用Ajax动态获取,所以我在后面写了方法专门获取学习课程人数,其中用到的Trim()方法是去除文本中的空格

获取学习课程的人数:

//获取上课人数
function getNumber(url){

    let datas = \'\';

    http.get(url,(res)=>{
        res.on(\'data\',(chunk)=>{
            datas += chunk;
        });

        res.on(\'end\',()=>{
            datas = JSON.parse(datas);
            courseMembers.push({\'id\':datas.data[0].id,\'numbers\':parseInt(datas.data[0].numbers,10)});
        });
    });
}

这样就将想获取课程的学习人数都添加到了courseMembers数组中,在最后将学习课程的人数在赋值给相对应的课程

        //给每个courseMenners.number赋值
        for(let i=0;i<videosId.length;i++){
            for(let j=0;j<videosId.length;j++){
                if(courseMembers[i].id +\'\' == videosId[j]){
                    courseData[j].number = courseMembers[i].numbers;
                }
            }
        }

 

我们获取到了数据,就要把它按照一定的格式存到一个文件中

//写入文件
function writeFile(file,string) {
    fs.appendFileSync(file,string,(err)=>{
            if(err){
                console.log(err);
            }
        })
}

//打印信息
function printfData(coursesData){

    coursesData.forEach((courseData)=>{
       // console.log(`${courseData.number}人学习过${courseData.title}\\n`);
       writeFile(outputFile,`\\n\\n${courseData.number}人学习过${courseData.title}\\n\\n`);

        courseData.videos.forEach(function(item){
            let chapterTitle = item.chapterTitle;
            // console.log(chapterTitle + \'\\n\');
            writeFile(outputFile,`\\n  ${chapterTitle}\\n`);

            item.video.forEach(function(item){
                // console.log(\'     【\' + item.id + \'】\' + item.title + \'\\n\');
                writeFile(outputFile,`     【${item.id}】  ${item.title}\\n`);
            })
        });

    });


}

 

最后获取到的数据:

 

源码:

/**
 * Created by hp-pc on 2017/6/7 0007.
 */
const http = require(\'http\');
const fs = require(\'fs\');
const cheerio = require(\'cheerio\');
const baseUrl = \'http://www.imooc.com/learn/\';
const baseNuUrl = \'http://www.imooc.com/course/AjaxCourseMembers?ids=\';
//获取课程的ID
const videosId = [773,371];
//输出的文件
const outputFile = \'test.txt\';
//记录学习课程的人数
let courseMembers = [];

//去除字符串中的空格
function Trim(str,is_global)
{
    let  result;
    result = str.replace(/(^\\s+)|(\\s+$)/g,"");
    if(is_global.toLowerCase()=="g")
    {
        result = result.replace(/\\s/g,"");
    }
    return result;
}

//接受一个url爬取整个网页,返回一个Promise对象
function getPageAsync(url){
    return new Promise((resolve,reject)=>{
        console.log(`正在爬取${url}的内容`);
        http.get(url,function(res){
            let html = \'\';

            res.on(\'data\',function(data){
                html += data;
            });

            res.on(\'end\',function(){
                resolve(html);
            });

            res.on(\'error\',function(err){
                reject(err);
                console.log(\'错误信息:\' + err);
            })
        });
    })
}

//接受一个爬取下来的网页内容,查找网页中需要的信息
function filterChapter(html){
    const $ = cheerio.load(html);

    //所有章
    const chapters = $(\'.chapter\');


    //课程的标题和学习人数
    let title = $(\'.hd>h2\').text();
    let number = 0;

    //最后返回的数据
    //每个网页需要的内容的结构
    let courseData = {
        \'title\':title,
        \'number\':number,
        \'videos\':[]
    };

    chapters.each(function(item){
        let chapter = $(this);
        //文章标题
        let chapterTitle = Trim(chapter.find(\'strong\').text(),\'g\');

        //每个章节的结构
        let chapterdata = {
            \'chapterTitle\':chapterTitle,
            \'video\':[]
        };


        //一个网页中的所有视频
        let videos = chapter.find(\'.video\').children(\'li\');
        videos.each(function(item){
            //视频标题
            let videoTitle = Trim($(this).find(\'a.J-media-item\').text(),\'g\');
            //视频ID
            let id = $(this).find(\'a\').attr(\'href\').split(\'video/\')[1];
            chapterdata.video.push({
                \'title\':videoTitle,
                \'id\':id
            })
        });

        courseData.videos.push(chapterdata);

    });

    return courseData;
}

//获取上课人数
function getNumber(url){

    let datas = \'\';

    http.get(url,(res)=>{
        res.on(\'data\',(chunk)=>{
            datas += chunk;
        });

        res.on(\'end\',()=>{
            datas = JSON.parse(datas);
            courseMembers.push({\'id\':datas.data[0].id,\'numbers\':parseInt(datas.data[0].numbers,10)});
        });
    });
}

//写入文件
function writeFile(file,string) {
    fs.appendFileSync(file,string,(err)=>{
            if(err){
                console.log(err);
            }
        })
}

//打印信息
function printfData(coursesData){

    coursesData.forEach((courseData)=>{
       // console.log(`${courseData.number}人学习过${courseData.title}\\n`);
       writeFile(outputFile,`\\n\\n${courseData.number}人学习过${courseData.title}\\n\\n`);

        courseData.videos.forEach(function(item){
            let chapterTitle = item.chapterTitle;
            // console.log(chapterTitle + \'\\n\');
            writeFile(outputFile,`\\n  ${chapterTitle}\\n`);

            item.video.forEach(function(item){
                // console.log(\'     【\' + item.id + \'】\' + item.title + \'\\n\');
                writeFile(outputFile,`     【${item.id}】  ${item.title}\\n`);
            })
        });

    });


}

//所有页面爬取完后返回的Promise数组
let courseArray = [];

//循环所有的videosId,和baseUrl进行字符串拼接,爬取网页内容
videosId.forEach((id)=>{
    //将爬取网页完毕后返回的Promise对象加入数组
    courseArray.push(getPageAsync(baseUrl + id));
    //获取学习的人数
    getNumber(baseNuUrl + id);
});

Promise
    //当所有网页的内容爬取完毕
    .all(courseArray)
    .then((pages)=>{
        //所有页面需要的内容
        let courseData = [];

        //遍历每个网页提取出所需要的内容
        pages.forEach((html)=>{
            let courses = filterChapter(html);
            courseData.push(courses);
        });

        //给每个courseMenners.number赋值
        for(let i=0;i<videosId.length;i++){
            for(let j=0;j<videosId.length;j++){
                if(courseMembers[i].id +\'\' == videosId[j]){
                    courseData[j].number = courseMembers[i].numbers;
                }
            }
        }

        //对所需要的内容进行排序
        courseData.sort((a,b)=>{
            return a.number > b.number;
        });

        //在重新将爬取内容写入文件中前,清空文件
        fs.writeFileSync(outputFile,\'###爬取慕课网课程信息###\',(err)=>{
            if(err){
                console.log(err)
            }
        });
        printfData(courseData);
    });

 

以上是关于Node.js爬虫-爬取慕课网课程信息的主要内容,如果未能解决你的问题,请参考以下文章

爬虫大作业

nodejs 爬虫笔记

python3爬虫第一天

慕课网学习node.js

Scrapy爬取美女图片 (原创)

Scrapy分布式爬虫打造搜索引擎(慕课网)--爬取知乎