zoukankan      html  css  js  c++  java
  • 【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')
    })

    结果如下:

     

  • 相关阅读:
    npm安装一直报错Error: Cannot find module 'lru-cache'
    vue使用ref获取元素
    WinForm 简易仿360界面控件
    【解决方案】macOS 打开微信视频电话其他应用音量变小问题
    ES服务的搭建(八)
    ES安装
    缓存一致性问题(七)
    多级缓存架构(六)
    掌握SKU和SPU关系及表设计(三)
    架构的搭建(一)
  • 原文地址:https://www.cnblogs.com/tgxh/p/7124202.html
Copyright © 2011-2022 走看看