1. 程式人生 > >Node.js爬蟲-爬取慕課網課程信息

Node.js爬蟲-爬取慕課網課程信息

reac 分享 function apt txt sta eject 賦值 find

第一次學習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爬蟲-爬取慕課網課程信息