用Promise實現隊列(爬一爬慕課網HTML代碼)

項目初始化

建立一個package.json文件,webstorm快捷建立package.json很是簡單。
使用 npm init 快速建立。css

工具模塊

須要下載的的模塊 html

這是2個npm包,咱們先下載:node

npm install superagent cheerio --save

須要引入的模塊 jquery

  • fs
  • path

引入項目依賴

const cheerio = require('cheerio');
const agent = require('superagent');
const path = require('path');
const fs = require('fs');

定義地址數組

咱們但願以隊列的形式逐個對這些地址進行訪問,獲取HTML代碼,以便後續處理:git

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"
}];

定義數據結構

慕課網課程列表:
clipboard.pnggithub

對此咱們定義以下的數據結構:web

[
     {
      page: 1,
      data: [
          {
             title:"", // 課程標題
             imgurl:"", // 課程圖片
             level:"", // 等級
             studynum:0, // 學習人數
             description:"xxxx" // 課程描述
        }
        ...... // 每個頁面有多條課程信息
      ]  
    }
    ...... // 一共有多個頁面
 ]

superagent 頁面數據下載

superagent是nodejs裏一個很是方便的客戶端請求代碼模塊,superagent是一個輕量級的,漸進式的ajax API,可讀性好,學習曲線低,內部依賴nodejs原生的請求API,適用於nodejs環境下。

基本使用方法:
具體的請自行點擊鏈接查看喲...ajax

request
    .get('/login')
    .end(function(err, res){
        // code
    });

cheerio 頁面數據解析

cheerio是一個node的庫,能夠理解爲一個Node.js版本的jquery,用來從網頁中以 css selector取數據,使用方式和jquery基本相同。

須要先loading一個須要加載html文檔,後面就能夠jQuery同樣使用操做頁面了。npm

基本使用方法:
具體的請自行點擊鏈接查看喲...json

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

能夠看到終端有次序的輸出瞭如下內容:

clipboard.png

當打開生成的 result.json 文件,其結構也符合咱們的預期:
clipboard.png

至此,這篇文章也就結束啦,若是您有好的想法請留言喲。

持續學習中...

相關文章
相關標籤/搜索