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 相关文章推荐
jQuery移动和复制dom节点实用DOM操作案例
Dec 17 Javascript
js和jquery对dom节点的操作(创建/追加)
Apr 21 Javascript
原生js实现fadein 和 fadeout淡入淡出效果
Jun 05 Javascript
在JS数组特定索引处指定位置插入元素的技巧
Aug 24 Javascript
node.js中格式化数字增加千位符的几种方法
Jul 03 Javascript
Jquery插件之Fancybox丰富的弹出层效果附源码下载
Dec 02 Javascript
JavaScript统计字符串中每个字符出现次数完整实例
Jan 28 Javascript
JavaScript获取当前运行脚本文件所在目录的方法
Feb 03 Javascript
jquery获取select,option所有的value和text的实例
Mar 06 Javascript
JS获取子、父、兄节点方法小结
Aug 14 Javascript
vue-cli中打包图片路径错误的解决方法
Oct 26 Javascript
vue cli安装使用less的教程详解
Jul 12 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实现密保卡功能实现代码&amp;lt;打包下载直接运行&amp;gt;
2011/10/09 PHP
[原创]php逐行读取txt文件写入数组的方法
2015/07/02 PHP
PHP命名空间namespace及use的简单用法分析
2018/08/03 PHP
niceTitle 基于jquery的超链接提示插件
2010/05/31 Javascript
jQuery处理图片加载失败的常用方法
2015/06/08 Javascript
javascript实现简单的分页特效
2015/08/12 Javascript
javascript仿百度输入框提示自动下拉补全
2016/01/07 Javascript
jQuery添加删除DOM元素方法详解
2016/01/18 Javascript
举例讲解如何判断JavaScript中对象的类型
2016/04/22 Javascript
JavaScript常用代码书写规范的超全面总结
2016/09/11 Javascript
node.js 利用流实现读写同步,边读边写的方法
2017/09/11 Javascript
Angular2开发环境搭建教程之VS Code
2017/12/15 Javascript
express 项目分层实践详解
2018/12/10 Javascript
2019年度web前端面试题总结(主要为Vue面试题)
2020/01/12 Javascript
JavaScript面向对象核心知识与概念归纳整理
2020/05/09 Javascript
javascript中layim之查找好友查找群组
2021/02/06 Javascript
Python使用pip安装报错:is not a supported wheel on this platform的解决方法
2018/01/23 Python
css 省略号 css3让多余的字符串消失并附加省略号的实现代码
2013/02/07 HTML / CSS
美国猫狗药物和用品网站:PetCareRx
2017/01/05 全球购物
Asics日本官网:鬼冢八喜郎创立的跑鞋运动品牌
2017/10/18 全球购物
美国廉价机票预订网站:Cheapfaremart
2018/04/28 全球购物
理工大学毕业生自荐信
2013/11/01 职场文书
致跳高运动员广播稿
2014/01/13 职场文书
初一生物教学反思
2014/01/18 职场文书
优秀士兵个人事迹材料
2014/01/19 职场文书
学校门卫管理制度
2014/01/30 职场文书
小学英语教学反思
2014/01/30 职场文书
总裁助理岗位职责
2014/02/17 职场文书
会计岗位描述
2014/02/22 职场文书
责任胜于能力演讲稿
2014/05/20 职场文书
教师作风整改措施思想汇报
2014/10/12 职场文书
北京爱情故事观后感
2015/06/12 职场文书
2015选调生工作总结
2015/07/24 职场文书
《落花生》教学反思
2016/02/16 职场文书
学校学习型党组织建设心得体会
2019/06/21 职场文书
关于MybatisPlus配置双数据库驱动连接数据库问题
2022/01/22 Java/Android