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 相关文章推荐
js创建数据共享接口——简化框架之间相互传值
Oct 23 Javascript
javascript面向对象之对象的深入理解
Jan 13 Javascript
jQuery中clone()方法用法实例
Jan 16 Javascript
Linux下编译安装php libevent扩展实例
Feb 14 Javascript
JavaScript中用toString()方法返回时间为字符串
Jun 12 Javascript
js立即执行函数: (function ( ){})( ) 与 (function ( ){}( )) 有什么区别?
Nov 18 Javascript
在Node.js中使用Javascript Generators详解
May 05 Javascript
vue.js默认路由不加载linkActiveClass问题的解决方法
Dec 11 Javascript
vue引入axios同源跨域问题
Sep 27 Javascript
原生js实现针对Dom节点的CRUD操作示例
Aug 26 Javascript
JavaScript监听键盘事件代码实现
Jun 03 Javascript
jQuery treeview树形结构应用
Mar 24 jQuery
基于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弹出对话框实现重定向代码
2014/01/23 PHP
php的ddos攻击解决方法
2015/01/08 PHP
Symfony学习十分钟入门经典教程
2016/02/03 PHP
Zend Framework教程之配置文件application.ini解析
2016/03/10 PHP
Laravel框架实现的rbac权限管理操作示例
2019/01/16 PHP
PHPUnit + Laravel单元测试常用技能
2019/11/06 PHP
经典的解除许多网站无法复制文字的绝招
2006/12/31 Javascript
Javascript load Page,load css,load js实现代码
2010/03/31 Javascript
Tips 带三角可关闭的文字提示
2010/10/06 Javascript
AngularJS使用angular-formly进行表单验证
2015/12/27 Javascript
JavaScript 冒泡排序和选择排序的实现代码
2016/09/03 Javascript
解决Vue不能检测数组或对象变动的问题
2018/02/24 Javascript
node.js使用express框架进行文件上传详解
2019/03/03 Javascript
python的迭代器与生成器实例详解
2014/07/16 Python
Python中常见的数据类型小结
2015/08/29 Python
利用matplotlib+numpy绘制多种绘图的方法实例
2017/05/03 Python
python实现比较文件内容异同
2018/06/22 Python
详解python中自定义超时异常的几种方法
2019/07/29 Python
pytorch forward两个参数实例
2020/01/17 Python
一款利用html5和css3动画排列人物头像的实例演示
2014/12/05 HTML / CSS
java程序员面试交流
2012/11/29 面试题
个人教师自我评价范文
2013/12/02 职场文书
优秀求职信范文分享
2013/12/19 职场文书
鲜花方阵解说词
2014/02/13 职场文书
公交公司毕业生求职信
2014/02/15 职场文书
2014道德模范事迹材料
2014/02/16 职场文书
大学生找工作求职信
2014/07/09 职场文书
医药销售自我评价200字
2014/09/11 职场文书
高中校园广播稿3篇
2014/09/29 职场文书
交通局领导班子群众路线教育实践活动对照检查材料思想汇报
2014/10/09 职场文书
小学师德师风整改措施
2014/10/27 职场文书
计算机实训报告总结
2014/11/05 职场文书
单位介绍信格式
2015/01/31 职场文书
2015年客服工作总结范文
2015/04/02 职场文书
小学班主任工作总结2015
2015/04/07 职场文书
房屋质量投诉书
2015/07/02 职场文书