【nodejs爬虫】使用async控制并发写一个小说爬虫
最近在做一個(gè)書城項(xiàng)目,數(shù)據(jù)用爬蟲爬取,百度了一下找到這個(gè)網(wǎng)站,以擇天記這本小說為例。
爬蟲用到了幾個(gè)模塊,cheerio,superagent,async。
superagent是一個(gè)http請求模塊,詳情可參考鏈接。
cheerio是一個(gè)有著jQuery類似語法的文檔解析模塊,你可以簡單理解為nodejs中的jQuery。
async是一個(gè)異步流程控制模塊,在這里我們主要用到async的mapLimit(coll, limit, iteratee, callback)
async.mapLimit(urls, 10, function (url, callback) {fetchUrl(url, callback, id)}, function (err, results) {//TODO})第一個(gè)參數(shù)coll是一個(gè)數(shù)組,保存了小說的章節(jié)url,第二個(gè)參數(shù)limit是控制并發(fā)數(shù),第三個(gè)參數(shù)iteratee接受一個(gè)回調(diào)函數(shù),該回調(diào)函數(shù)的第一個(gè)參數(shù)就是單獨(dú)某一章的url,第二個(gè)參數(shù)也是一個(gè)回調(diào)函數(shù),這個(gè)回調(diào)函數(shù)執(zhí)行后會(huì)把結(jié)果(在這里就是每一章的內(nèi)容)保存到第四個(gè)參數(shù)callback的results中,results是一個(gè)數(shù)組,保存了所有章節(jié)的內(nèi)容。
我們在fetchUrl獲取章節(jié)數(shù)據(jù)。
?
首先我們要根據(jù)小說的主頁url獲取所有章節(jié)的url保存到數(shù)組urls中:
superagent.get(url).charset('gbk') //該網(wǎng)站編碼為gbk,用到了superagent-charset.end(function (err, res) {var $ = cheerio.load(res.text); //res.text為獲取的網(wǎng)頁內(nèi)容,通過cheerio的load方法處理后,之后就是jQuery的語法了let urls = []total = $('#list dd').lengthconsole.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函數(shù)
function fetchUrl(url, callback, id) {superagent.get(url).charset('gbk').end(function (err, res) {let $ = cheerio.load(res.text)//obj為構(gòu)建的包含章節(jié)信息的對象callback(null, obj) //將obj傳遞給第四個(gè)參數(shù)中的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 //總章節(jié)數(shù) let id = 0 //計(jì)數(shù)器 const chapters = 10 //爬取多少章 const url = 'http://www.zwdu.com/book/8634/'//去除前后空格和 轉(zhuǎn)義字符 function trim(str) {return str.replace(/(^\s*)|(\s*$)/g, '').replace(/ /g, '') }//將Unicode轉(zhuǎn)漢字 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())//分析結(jié)構(gòu)后分割htmlconst 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中,不支持直接保存數(shù)組,所以將數(shù)組拼接成字符串,取出時(shí)再分割字符串即可}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').lengthconsole.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) //需要對章節(jié)編號(hào),所以通過變量id來計(jì)數(shù)}, function (err, results) {response.send(results)})}) })app.listen(3378, function () {console.log('server listening on 3378') })結(jié)果如下:
?
?
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/tgxh/p/7124202.html
總結(jié)
以上是生活随笔為你收集整理的【nodejs爬虫】使用async控制并发写一个小说爬虫的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: js == 和 ===
- 下一篇: 移动端自适应