Node.js 实现简单小说爬虫实例


Posted in Javascript onNovember 18, 2016

最近因为剧荒,老大追了爱奇艺的一部网剧,由丁墨的同名小说《美人为馅》改编,目前已经放出两季,虽然整部剧槽点满满,但是老大看得不亦乐乎,并且在看完第二季之后跟我要小说资源,直接要奔原著去看结局……

随手搜了下,都是在线资源,下载的话需要登录,注册登录好麻烦,写个爬虫玩玩也好,于是动手用 node 写了一个,这里做下笔记

工作流程

  • 获取 URLs 列表(请求资源 request模块)
  • 根据 URLs 列表获取相关页面源码(可能遇到页面编码问题,iconv-lite模块)
  • 源码解析,获取小说信息( cheerio模块)
  • 保存小说信息到 Markdown 文件,并且加适当修饰以及章节信息(写文件 fs、同步请求资源 sync-request 模块)
  • Markdown 转 PDF (使用 Pandoc 或者 Chrome 的打印功能)

获取 URLs

根据小说的导航页,获取小说所有章节的 URL,并且以 JSON 数组的方式存储。

  • 首选通过 http.get() 方法获取页面源码
  • 获取到源码,打印发现中文乱码,查看发现 charset = 'gbk',需要进行转码
  • 使用 iconv-lite 模块进行转码,中文显示正常后开始解析源码,获取需要的 URL,为了更方便地解析,需要引进 cheerio 模块,cheerio 可以理解为运行在后台的 jQuery,用法与 jQuery 也十分相似,熟悉 jQuery 的同学可以很快的上手
  • 将源码加载进 cheerio,分析了源码后得知所有章节信息都存于被 div 包裹的 a 标签中,通过 cheerio 取出符合条件的 a 标签组,进行遍历,获取章节的 title 和 URL,保存为对象,存进数组,(因为链接中存储的 URL 不完整,所以存储时需要补齐)
  • 将对象数组序列化,写进 list.json 文件
var http = require("http")
var fs = require("fs")
var cheerio = require("cheerio")
var iconv = require("iconv-lite")
var url = 'http://www.17fa.com/files/article/html/90/90747/index.html'
http.get(url, function(res) { //资源请求
  var chunks = []
  res.on('data', function(chunk) {
    chunks.push(chunk)
  })
  res.on('end', function() {
    var html = iconv.decode(Buffer.concat(chunks), 'gb2312') //转码操作
    var $ = cheerio.load(html, {
      decodeEntities: false
    })
    var content = $("tbody")
    var links = []
    $('div').children('a').each(function(i, elem) {
      var link = new Object()
      link.title = $(this).text()
      link.link = 'http://www.17fa.com/files/article/html/90/90747/' + $(this).attr('href') //补齐 URL 信息
      if (i > 5) {
        links.push(link)
      }
    })
    fs.writeFile("list.json", JSON.stringify(links), function(err) {
      if (!err) {
        console.log("写文件成功")
      }
    })
  }).on('error', function() {
    console.log("网页访问出错")
  })
})

获取的列表示例

[{
  "title": "3 法医司白",
  "link": "http://www.17fa.com/files/article/html/90/90747/16548771.html"
}, {
  "title": "4 第1个梦 ",
  "link": "http://www.17fa.com/files/article/html/90/90747/16548772.html"
}, {
  "title": "5 刑警韩沉 ",
  "link": "http://www.17fa.com/files/article/html/90/90747/16548773.html"
}, {
  "title": "6 最初之战",
  "link": "http://www.17fa.com/files/article/html/90/90747/16548774.html "
}]

获取数据

有了 URLs 列表,接下来的工作就很机械了,遍历 URLs 列表请求资源,获取源码,解析源码,获取小说,写文件,但是,因为最终将所有的章节保存入一个文件,要保证章节的顺序,因此写文件需要 同步操作,实际上,我在编码的时候所有的操作都改成了同步方式

获取源码

通过解析读取的 list.json 文件,获取到 URLs 列表,遍历列表获取资源,因为需要确保章节的顺序,所以这里引进 sync-request 模块进行同步 request 请求资源,请求资源后照例转码

var http = require("http")
var fs = require("fs")
var cheerio = require("cheerio")
var iconv = require("iconv-lite")
var request = require('sync-request')
var urlList = JSON.parse(fs.readFileSync('list.json', 'utf8'))
function getContent(chapter) {
  var res = request('GET',chapter.link)
  var html = iconv.decode(res.body, 'gb2312') //获取源码
}
for (let i = 0; i < urlList.length; i++) {
  getContent(urlList[i])
}

解析源码,获取小说

还是通过 cheerio 模块获取小说内容,避免影响观感,写操作之前去除内容中的的 html 标签

function getContent(chapter) {
  var res = request('GET',chapter.link)
  var html = iconv.decode(res.body, 'gb2312')
  var $ = cheerio.load(html, {
    decodeEntities: false
  })
  var content = ($("div#r1c").text()).replace(/\ /g, '')
}

保存小说

写操作也需要同步操作,因此使用了同步写函数 fs.writeFileSync() 和 同步添加函数 fs.appendFileSync(),第一次写使用写函数,之后的内容都是进行 append 操作,为了改善阅读体验,每个章节前添加标题

也可以在内容前添加 拍 [TOC],作为导航链接Node.js 实现简单小说爬虫实例

var http = require("http")
var fs = require("fs")
var cheerio = require("cheerio")
var iconv = require("iconv-lite")
var path = require('path')
var urlList = JSON.parse(fs.readFileSync('list.json', 'utf8'))
function getContent(chapter) {
  console.log(chapter.link)
  http.get(chapter.link, function(res) {
    var chunks = []
    res.on('data', function(chunk) {
      chunks.push(chunk)
    })
    res.on('end', function() {
      var html = iconv.decode(Buffer.concat(chunks), 'gb2312')
      var $ = cheerio.load(html, {
        decodeEntities: false
      })
      var content = ($("div#r1c").text()).replace(/\ /g, '')
      if (fs.existsSync('美人为馅.md')) {
        fs.appendFileSync('美人为馅.md', '### ' + chapter.title)
        fs.appendFileSync('美人为馅.md', content)
      } else {
        fs.writeFileSync('美人为馅.md', '### ' + chapter.title)
        fs.appendFileSync('美人为馅.md', content)
      }
    })
  }).on('error', function() {
    console.log("爬取" + chapter.link + "链接出错!")
  })
}
for (let i = 0; i < urlList.length; i++) {
  console.log(urlList[i])
  getContent(urlList[i])
}

Markdown 转 PDF

我将小说保存在 Markdown 文件中,为了提升阅读体验,可以将 Markdown 文件转换成 PDF 文件,目前我较为喜欢的两种方式,通过 Chrome 的打印功能 以及 pandoc 转换

Chrome 打印

SublimeText 有个插件 markdown preview ,可通过 Alt + m 快捷键在 Chrome 中预览 Markdown,在 Chrome 页面中右键,选择打印,调整好参数后,选择另存为 PDF,简单,粗暴,深得我心

打印效果:Node.js 实现简单小说爬虫实例

pandoc 转换
pandoc 是十分强大的文件格式转换工具,可以将 Markdown 文件转换成多种格式,今晚在 windows10 下折腾了半天,始终检索不到 pdflatex,关于 pandoc,后面会专门写一篇总结。

PDF 已经发给老大了,现在正在看

关于python、node、爬虫

在之前很长的一段时间里,很想用 Python,很想写爬虫,更想用 Python 写爬虫,甚至成为了心里的一块执念,随着接触的知识更全面,执念也逐渐淡去,少了很多“想”,遇事想着多去动手,实践出真知。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JavaScript学习点滴 call、apply的区别
Oct 22 Javascript
基于jQuery的获得各种控件Value的方法
Nov 19 Javascript
8个超棒的学习 jQuery 的网站 推荐收藏
Apr 02 Javascript
JQuery实现简单时尚快捷的气泡提示插件
Dec 20 Javascript
iScroll中事件点击触发两次解决方案
Mar 11 Javascript
js 倒计时(高效率服务器时间同步)
Sep 12 Javascript
Vue.js项目中管理每个页面的头部标签的两种方法
Jun 25 Javascript
Echarts实现多条折线可拖拽效果
Dec 19 Javascript
JavaScript组合模式---引入案例分析
May 23 Javascript
微信小程序开发(一):服务器获取数据列表渲染操作示例
Jun 01 Javascript
vue组件暴露和.js文件暴露接口操作
Aug 11 Javascript
谈谈JavaScript令人迷惑的==与+
Aug 31 Javascript
基于jQuery的select下拉框选择触发事件实例分析
Nov 18 #Javascript
jQuery实现隔行变色的方法分析(对比原生JS)
Nov 18 #Javascript
jQuery和JavaScript节点插入元素的方法对比
Nov 18 #Javascript
js与jquery分别实现tab标签页功能的方法
Nov 18 #Javascript
jQuery实现点击某个div打开层,点击其他div关闭层实例分析(阻止冒泡)
Nov 18 #Javascript
浅谈javascript中遇到的字符串对象处理
Nov 18 #Javascript
文件上传,iframe跨域数据提交的实现
Nov 18 #Javascript
You might like
第三章 php操作符与控制结构代码
2011/12/30 PHP
PHP原生函数一定好吗?
2014/12/08 PHP
php对数组内元素进行随机调换的方法
2015/05/12 PHP
PHP发送邮件确认验证注册功能示例【修改别人邮件类】
2019/11/09 PHP
jquery用data方法获取某个元素上的事件
2014/06/23 Javascript
使用jQueryMobile实现滑动翻页效果的方法
2015/02/04 Javascript
js+html5实现canvas绘制简单矩形的方法
2015/06/05 Javascript
第二章之Bootstrap 页面排版样式
2016/04/25 Javascript
基于Bootstrap重置输入框内容按钮插件
2016/05/12 Javascript
jQuery获取浏览器类型和版本号的方法
2016/07/05 Javascript
详解js界面跳转与值传递
2016/11/22 Javascript
Layui table 组件的使用之初始化加载数据、数据刷新表格、传参数
2017/09/11 Javascript
浅谈vue2 单页面如何设置网页title
2017/11/08 Javascript
swiper在angularjs中使用循环轮播失效的解决方法
2018/09/27 Javascript
mpvue微信小程序多列选择器用法之省份城市选择的实现
2019/03/07 Javascript
基于javascript实现碰撞检测
2020/03/12 Javascript
vue项目实现多语言切换的思路
2020/09/17 Javascript
在vue中实现某一些路由页面隐藏导航栏的功能操作
2020/09/21 Javascript
Vue+element-ui添加自定义右键菜单的方法示例
2020/12/08 Vue.js
python使用super()出现错误解决办法
2017/08/14 Python
python pcm音频添加头转成Wav格式文件的方法
2019/01/09 Python
Python基于OpenCV实现人脸检测并保存
2019/07/23 Python
Python中的list与tuple集合区别解析
2019/10/12 Python
tensorflow实现训练变量checkpoint的保存与读取
2020/02/10 Python
Python线程协作threading.Condition实现过程解析
2020/03/12 Python
Python二元算术运算常用方法解析
2020/09/15 Python
使用html2canvas将页面转成图并使用用canvas2image下载
2019/04/04 HTML / CSS
在html5的Canvas上绘制椭圆的几种方法总结
2013/01/07 HTML / CSS
服装销售人员求职自我评价
2013/09/26 职场文书
2013英文求职信模板范文
2013/11/15 职场文书
电厂职工自我鉴定
2014/02/20 职场文书
出国英文推荐信
2014/05/10 职场文书
企业员工薪酬方案
2014/06/04 职场文书
2015年社区工会工作总结
2015/05/26 职场文书
浅谈Node的内存泄露问题
2022/05/06 NodeJs
vue css 相对路径导入问题级踩坑记录
2022/06/05 Vue.js