【nodejs爬蟲】使用async控制並發寫一個小說爬蟲


最近在做一個書城項目,數據用爬蟲爬取,百度了一下找到這個網站,以擇天記這本小說為例。

爬蟲用到了幾個模塊,cheerio,superagent,async。

superagent是一個http請求模塊,詳情可參考鏈接

cheerio是一個有着jQuery類似語法的文檔解析模塊,你可以簡單理解為nodejs中的jQuery。

async是一個異步流程控制模塊,在這里我們主要用到async的mapLimit(coll, limit, iteratee, callback)

async.mapLimit(urls, 10, function (url, callback) {
        fetchUrl(url, callback, id)
      }, function (err, results) {
        //TODO
      })

第一個參數coll是一個數組,保存了小說的章節url,第二個參數limit是控制並發數,第三個參數iteratee接受一個回調函數,該回調函數的第一個參數就是單獨某一章的url,第二個參數也是一個回調函數,這個回調函數執行后會把結果(在這里就是每一章的內容)保存到第四個參數callback的results中,results是一個數組,保存了所有章節的內容。

我們在fetchUrl獲取章節數據。

 

首先我們要根據小說的主頁url獲取所有章節的url保存到數組urls中:

superagent.get(url)
    .charset('gbk')  //該網站編碼為gbk,用到了superagent-charset
    .end(function (err, res) {
      var $ = cheerio.load(res.text); //res.text為獲取的網頁內容,通過cheerio的load方法處理后,之后就是jQuery的語法了
      let urls = []
      total = $('#list dd').length
      console.log(`共${$('#list dd').length}章`)
      $('#list dd').each(function (i, v) {
        if (i < chapters) {
          urls.push('http://www.zwdu.com' + $(v).find('a').attr('href'))
        }
      })

fetchUrl函數

function fetchUrl(url, callback, id) {
  superagent.get(url)
    .charset('gbk')
    .end(function (err, res) {
      let $ = cheerio.load(res.text)
      //obj為構建的包含章節信息的對象
      callback(null, obj)  //將obj傳遞給第四個參數中的results
    })
}

完整代碼:

/**
 * Created by tgxh on 2017/7/4.
 */
const cheerio = require('cheerio')
const express = require('express')
const app = express()
const superagent = require('superagent')
require('superagent-charset')(superagent)
const async = require('async');

let total = 0 //總章節數
let id = 0 //計數器
const chapters = 10 //爬取多少章
const url = 'http://www.zwdu.com/book/8634/'

//去除前后空格和&nbsp;轉義字符
function trim(str) {
  return str.replace(/(^\s*)|(\s*$)/g, '').replace(/&nbsp;/g, '')
}

//將Unicode轉漢字
function reconvert(str) {
  str = str.replace(/(&#x)(\w{1,4});/gi, function ($0) {
    return String.fromCharCode(parseInt(escape($0).replace(/(%26%23x)(\w{1,4})(%3B)/g, "$2"), 16));
  });
  return str
}

function fetchUrl(url, callback, id) {
  superagent.get(url)
    .charset('gbk')
    .end(function (err, res) {
      let $ = cheerio.load(res.text)
      const arr = []
      const content = reconvert($("#content").html())
      //分析結構后分割html
      const contentArr = content.split('<br><br>')
      contentArr.forEach(elem => {
        const data = trim(elem.toString())
        arr.push(data)
      })
      const obj = {
        id: id,
        err: 0,
        bookName: $('.footer_cont a').text(),
        title: $('.bookname h1').text(),
        content: arr.join('-')  //由於需要保存至mysql中,不支持直接保存數組,所以將數組拼接成字符串,取出時再分割字符串即可
      }
      callback(null, obj)  
    })
}

app.get('/', function (req, response, next) {
  superagent.get(url)
    .charset('gbk')
    .end(function (err, res) {
      var $ = cheerio.load(res.text);
      let urls = []
      total = $('#list dd').length
      console.log(`共${$('#list dd').length}章`)
      $('#list dd').each(function (i, v) {
        if (i < chapters) {
          urls.push('http://www.zwdu.com' + $(v).find('a').attr('href'))
        }
      })

      async.mapLimit(urls, 10, function (url, callback) {
        id++
        fetchUrl(url, callback, id) //需要對章節編號,所以通過變量id來計數
      }, function (err, results) {
        response.send(results)
      })
    })
})

app.listen(3378, function () {
  console.log('server listening on 3378')
})

結果如下:

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM