第一次學習Node.js爬蟲,因此這時一個簡單的爬蟲,Node.js的好處就是能夠併發的執行html
這個爬蟲主要就是獲取慕課網的課程信息,並把得到的信息存儲到一個文件中,其中要用到cheerio庫,它能夠讓咱們方便的操做HTML,就像是用jQ同樣npm
開始前,記得數組
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拼接起來就是課程的地址app
const baseUrl = 'http://www.imooc.com/learn/'; const baseNuUrl = 'http://www.imooc.com/course/AjaxCourseMembers?ids='; //獲取課程的ID const videosId = [773,371];
爲了使獲取每一個課程內容時併發執行,要使用Promise中的all方法ide
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()方法是去除文本中的空格學習
獲取學習課程的人數:ui
//獲取上課人數 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數組中,在最後將學習課程的人數在賦值給相對應的課程this
//給每一個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); });