我的编程空间,编程开发者的网络收藏夹
学习永远不晚

怎么使用NodeJs爬虫抓取古代典籍

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

怎么使用NodeJs爬虫抓取古代典籍

这篇文章主要讲解了“怎么使用NodeJs爬虫抓取古代典籍”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么使用NodeJs爬虫抓取古代典籍”吧!

项目实现方案分析

项目是一个典型的多级抓取案例,目前只有三级,即 书籍列表, 书籍项对应的 章节列表,一个章节链接对应的内容。 抓取这样的结构可以采用两种方式, 一是  直接从外层到内层 内层抓取完以后再执行下一个外层的抓取,  还有一种就是先把外层抓取完成保存到数据库,然后根据外层抓取到所有内层章节的链接,再次保存,然后从数据库查询到对应的链接单元  对之进行内容抓取。这两种方案各有利弊,其实两种方式我都试过, 后者有一个好处,因为对三个层级是分开抓取的,  这样就能够更方便,尽可能多的保存到对应章节的相关数据。 可以试想一下 ,如果采用前者 按照正常的逻辑

对一级目录进行遍历抓取到对应的二级章节目录, 再对章节列表进行遍历 抓取内容,到第三级 内容单元抓取完成 需要保存时,如果需要很多的一级目录信息,就需要  这些分层的数据之间进行数据传递 ,想想其实应该是比较复杂的一件事情。所以分开保存数据 一定程度上避开了不必要的复杂的数据传递。

目前我们考虑到 其实我们要抓取到的古文书籍数量并不多,古文书籍大概只有180本囊括了各种经史。其和章节内容本身是一个很小的数据  ,即一个集合里面有180个文档记录。  这180本书所有章节抓取下来一共有一万六千个章节,对应需要访问一万六千个页面爬取到对应的内容。所以选择第二种应该是合理的。

项目实现

主程有三个方法 bookListInit ,chapterListInit,contentListInit,  分别是抓取书籍目录,章节列表,书籍内容的方法对外公开暴露的初始化方法。通过async  可以实现对这三个方法的运行流程进行控制,书籍目录抓取完成将数据保存到数据库,然后执行结果返回到主程序,如果运行成功  主程序则执行根据书籍列表对章节列表的抓取,同理对书籍内容进行抓取。

项目主入口

 const start = async() => {     let booklistRes = await bookListInit();     if (!booklistRes) {         logger.warn('书籍列表抓取出错,程序终止...');         return;     }     logger.info('书籍列表抓取成功,现在进行书籍章节抓取...');      let chapterlistRes = await chapterListInit();     if (!chapterlistRes) {         logger.warn('书籍章节列表抓取出错,程序终止...');         return;     }     logger.info('书籍章节列表抓取成功,现在进行书籍内容抓取...');      let contentListRes = await contentListInit();     if (!contentListRes) {         logger.warn('书籍章节内容抓取出错,程序终止...');         return;     }     logger.info('书籍内容抓取成功'); } // 开始入口 if (typeof bookListInit === 'function' && typeof chapterListInit === 'function') {     // 开始抓取     start(); }

引入的 bookListInit ,chapterListInit,contentListInit, 三个方法

booklist.js

 const chapterListInit = async() => {     const list = await bookHelper.getBookList(bookListModel);     if (!list) {         logger.error('初始化查询书籍目录失败');     }     logger.info('开始抓取书籍章节列表,书籍目录共:' + list.length + '条');     let res = await asyncGetChapter(list);     return res; };

chapterlist.js

 const contentListInit = async() => {     //获取书籍列表     const list = await bookHelper.getBookLi(bookListModel);     if (!list) {         logger.error('初始化查询书籍目录失败');         return;     }     const res = await mapBookList(list);     if (!res) {         logger.error('抓取章节信息,调用 getCurBookSectionList() 进行串行遍历操作,执行完成回调出错,错误信息已打印,请查看日志!');         return;     }     return res; }

内容抓取的思考

书籍目录抓取其实逻辑非常简单,只需要使用async.mapLimit做一个遍历就可以保存数据了,但是我们在保存内容的时候 简化的逻辑其实就是 遍历章节列表  抓取链接里的内容。但是实际的情况是链接数量多达几万 我们从内存占用角度也不能全部保存到一个数组中,然后对其遍历,所以我们需要对内容抓取进行单元化。

普遍的遍历方式 是每次查询一定的数量,来做抓取,这样缺点是只是以一定数量做分类,数据之间没有关联,以批量方式进行插入,如果出错  则容错会有一些小问题,而且我们想一本书作为一个集合单独保存会遇到问题。因此我们采用第二种就是以一个书籍单元进行内容抓取和保存。

这里使用了 async.mapLimit(list, 1, (series, callback) => {})  这个方法来进行遍历,不可避免的用到了回调,感觉很恶心。async.mapLimit()的第二个参数可以设置同时请求数量。

   const contentListInit = async() => {     //获取书籍列表     const list = await bookHelper.getBookList(bookListModel);     if (!list) {         logger.error('初始化查询书籍目录失败');         return;     }     const res = await mapBookList(list);     if (!res) {         logger.error('抓取章节信息,调用 getCurBookSectionList() 进行串行遍历操作,执行完成回调出错,错误信息已打印,请查看日志!');         return;     }     return res; }  const mapBookList = (list) => {     return new Promise((resolve, reject) => {         async.mapLimit(list, 1, (series, callback) => {             let doc = series._doc;             getCurBookSectionList(doc, callback);         }, (err, result) => {             if (err) {                 logger.error('书籍目录抓取异步执行出错!');                 logger.error(err);                 reject(false);                 return;             }             resolve(true);         })     }) }   const getCurBookSectionList = async(series, callback) => {      let num = Math.random() * 1000 + 1000;     await sleep(num);     let key = series.key;     const res = await bookHelper.querySectionList(chapterListModel, {         key: key     });     if (!res) {         logger.error('获取当前书籍: ' + series.bookName + ' 章节内容失败,进入下一部书籍内容抓取!');         callback(null, null);         return;     }     //判断当前数据是否已经存在     const bookItemModel = getModel(key);     const contentLength = await bookHelper.getCollectionLength(bookItemModel, {});     if (contentLength === res.length) {         logger.info('当前书籍:' + series.bookName + '数据库已经抓取完成,进入下一条数据任务');         callback(null, null);         return;     }     await mapSectionList(res);     callback(null, null); }

数据抓取完了 怎么保存是个问题

这里我们通过key 来给数据做分类,每次按照key来获取链接,进行遍历,这样的好处是保存的数据是一个整体,现在思考数据保存的问题

1、可以以整体的方式进行插入

优点 : 速度快 数据库操作不浪费时间。

缺点 : 有的书籍可能有几百个章节 也就意味着要先保存几百个页面的内容再进行插入,这样做同样很消耗内存,有可能造成程序运行不稳定。

2、可以以每一篇文章的形式插入数据库。

优点 : 页面抓取即保存的方式 使得数据能够及时保存,即使后续出错也不需要重新保存前面的章节,

缺点 : 也很明显 就是慢 ,仔细想想如果要爬几万个页面 做 几万次*N 数据库的操作 这里还可以做一个缓存器一次性保存一定条数  当条数达到再做保存这样也是一个不错的选择。

 const mapSectionList = (list) => {     return new Promise((resolve, reject) => {         async.mapLimit(list, 1, (series, callback) => {             let doc = series._doc;             getContent(doc, callback)         }, (err, result) => {             if (err) {                 logger.error('书籍目录抓取异步执行出错!');                 logger.error(err);                 reject(false);                 return;             }             const bookName = list[0].bookName;             const key = list[0].key;              // 以整体为单元进行保存             saveAllContentToDB(result, bookName, key, resolve);              //以每篇文章作为单元进行保存             // logger.info(bookName + '数据抓取完成,进入下一部书籍抓取函数...');             // resolve(true);          })     }) }

两者各有利弊,这里我们都做了尝试。 准备了两个错误保存的集合,errContentModel, errorCollectionModel,在插入出错时  分别保存信息到对应的集合中,二者任选其一即可。增加集合来保存数据的原因是 便于一次性查看以及后续操作, 不用看日志。

(PS ,其实完全用 errorCollectionModel 这个集合就可以了 ,errContentModel这个集合可以完整保存章节信息)

//保存出错的数据名称 const errorSpider = mongoose.Schema({     chapter: String,     section: String,     url: String,     key: String,     bookName: String,     author: String, }) // 保存出错的数据名称 只保留key 和 bookName信息 const errorCollection = mongoose.Schema({     key: String,     bookName: String, })

我们将每一条书籍信息的内容 放到一个新的集合中,集合以key来进行命名。

感谢各位的阅读,以上就是“怎么使用NodeJs爬虫抓取古代典籍”的内容了,经过本文的学习后,相信大家对怎么使用NodeJs爬虫抓取古代典籍这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

怎么使用NodeJs爬虫抓取古代典籍

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

怎么用python爬虫抓取网页文本

使用Python爬虫抓取网页文本可以使用第三方库requests和beautifulsoup。首先,安装requests和beautifulsoup库:pip install requestspip install beautifulsou
2023-10-18

怎么用Python爬虫抓取智联招聘

今天就跟大家聊聊有关怎么用Python爬虫抓取智联招聘,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。对于每个上班族来说,总要经历几次换工作,如何在网上挑到心仪的工作?如何提前为心仪工
2023-06-17

python爬虫时怎么使用R连续抓取多个页面

这篇文章将为大家详细讲解有关python爬虫时怎么使用R连续抓取多个页面,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。当抓取多页的html数据,但容易被困在通用方法部分的功能上,而导致无法实现连续抓取多个
2023-06-20

Python爬虫之怎么使用BeautifulSoup和Requests抓取网页数据

这篇文章主要介绍了Python爬虫之怎么使用BeautifulSoup和Requests抓取网页数据的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Python爬虫之怎么使用BeautifulSoup和Reque
2023-07-05

怎么使用python爬虫爬取数据

本篇内容介绍了“怎么使用python爬虫爬取数据”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!python爬出六部曲第一步:安装reques
2023-06-29

使用爬虫怎么获取代理服务器ip

使用爬虫怎么获取代理服务器ip?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。1、使用爬虫脚本每天定期抓取代理网站上的ip,并将其写入MongoDB或其他数据库。这个表格是原始表
2023-06-14

怎么使用爬虫代理IP

本篇内容介绍了“怎么使用爬虫代理IP”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!在网络数据时代,许多信息需要整理和使用。然而,面对如此大量
2023-06-20

使用Nodejs怎么编写一个定时爬虫

使用Nodejs怎么编写一个定时爬虫?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。const axios = require(axios)const roomid = "1
2023-06-09

怎么使用Java爬虫批量爬取图片

本篇内容主要讲解“怎么使用Java爬虫批量爬取图片”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么使用Java爬虫批量爬取图片”吧!爬取思路对于这种图片的获取,其实本质上就是就是文件的下载(H
2023-07-06

爬虫怎么使用ip代理池

这篇文章主要讲解了“爬虫怎么使用ip代理池”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“爬虫怎么使用ip代理池”吧!1、利用IP代理池技术,每次从IP代理池中随机选择一个IP代理来爬取数据。
2023-06-20

怎么使用python爬虫爬取二手房数据

这篇文章主要介绍怎么使用python爬虫爬取二手房数据,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!python的数据类型有哪些?python的数据类型:1. 数字类型,包括int(整型)、long(长整型)和flo
2023-06-14

使用Python爬虫怎么避免频繁爬取网站

这期内容当中小编将会给大家带来有关使用Python爬虫怎么避免频繁爬取网站,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。python的数据类型有哪些?python的数据类型:1. 数字类型,包括int(整
2023-06-15

使用python爬虫怎么获取表情包

今天就跟大家聊聊有关使用python爬虫怎么获取表情包,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。1、创建请求头,也被称为伪装浏览器如果不添加请求头的话,可能会出现当前网站没有访问
2023-06-15

怎么使用爬虫代理ip避免被封

这篇文章主要为大家展示了“怎么使用爬虫代理ip避免被封”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“怎么使用爬虫代理ip避免被封”这篇文章吧。1、正确处理cookie,可以避免很多收集问题,建议
2023-06-15

使用爬虫时代理ip不足怎么办

这篇文章主要讲解了“使用爬虫时代理ip不足怎么办”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“使用爬虫时代理ip不足怎么办”吧!爬虫在工作过程中,经常被目标网站禁止访问,但是找不到原因,很烦
2023-06-20

python爬虫之代理ip怎么正确使用

在Python中使用代理IP进行爬虫需要使用第三方库,比如requests库。以下是使用代理IP的正确步骤:1. 导入requests库和random库(用于随机选择代理IP):```pythonimport requestsimport
2023-08-17

在Python爬虫过程中怎么使用代理IP

这篇文章主要介绍了在Python爬虫过程中怎么使用代理IP,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。python是什么意思Python是一种跨平台的、具有解释性、编译性、
2023-06-14

怎么使用nodejs实现一个简单的网页爬虫功能

这篇文章主要介绍了怎么使用nodejs实现一个简单的网页爬虫功能,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。网页源码  使用http.get()方法获取网页源码,以hao1
2023-06-06

编程热搜

目录