Node.js 實(shí)現(xiàn)簡(jiǎn)單小說爬蟲實(shí)例
最近因?yàn)閯』?,老大追了愛奇藝的一部網(wǎng)劇,由丁墨的同名小說《美人為餡》改編,目前已經(jīng)放出兩季,雖然整部劇槽點(diǎn)滿滿,但是老大看得不亦樂乎,并且在看完第二季之后跟我要小說資源,直接要奔原著去看結(jié)局……
隨手搜了下,都是在線資源,下載的話需要登錄,注冊(cè)登錄好麻煩,寫個(gè)爬蟲玩玩也好,于是動(dòng)手用 node 寫了一個(gè),這里做下筆記
工作流程
- 獲取 URLs 列表(請(qǐng)求資源
request
模塊) - 根據(jù) URLs 列表獲取相關(guān)頁面源碼(可能遇到頁面編碼問題,
iconv-lite
模塊) - 源碼解析,獲取小說信息(
cheerio
模塊) - 保存小說信息到 Markdown 文件,并且加適當(dāng)修飾以及章節(jié)信息(寫文件
fs
、同步請(qǐng)求資源sync-request
模塊) - Markdown 轉(zhuǎn) PDF (使用 Pandoc 或者 Chrome 的打印功能)
獲取 URLs
根據(jù)小說的導(dǎo)航頁,獲取小說所有章節(jié)的 URL,并且以 JSON 數(shù)組的方式存儲(chǔ)。
- 首選通過
http.get()
方法獲取頁面源碼 - 獲取到源碼,打印發(fā)現(xiàn)中文亂碼,查看發(fā)現(xiàn)
charset = 'gbk'
,需要進(jìn)行轉(zhuǎn)碼 - 使用
iconv-lite
模塊進(jìn)行轉(zhuǎn)碼,中文顯示正常后開始解析源碼,獲取需要的 URL,為了更方便地解析,需要引進(jìn)cheerio
模塊,cheerio
可以理解為運(yùn)行在后臺(tái)的 jQuery,用法與 jQuery 也十分相似,熟悉 jQuery 的同學(xué)可以很快的上手 - 將源碼加載進(jìn)
cheerio
,分析了源碼后得知所有章節(jié)信息都存于被div
包裹的a
標(biāo)簽中,通過cheerio
取出符合條件的a
標(biāo)簽組,進(jìn)行遍歷,獲取章節(jié)的 title 和 URL,保存為對(duì)象,存進(jìn)數(shù)組,(因?yàn)殒溄又写鎯?chǔ)的 URL 不完整,所以存儲(chǔ)時(shí)需要補(bǔ)齊) - 將對(duì)象數(shù)組序列化,寫進(jìn)
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) { //資源請(qǐng)求 var chunks = [] res.on('data', function(chunk) { chunks.push(chunk) }) res.on('end', function() { var html = iconv.decode(Buffer.concat(chunks), 'gb2312') //轉(zhuǎn)碼操作 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') //補(bǔ)齊 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("網(wǎng)頁訪問出錯(cuò)") }) })
獲取的列表示例
[{ "title": "3 法醫(yī)司白", "link": "http://www.17fa.com/files/article/html/90/90747/16548771.html" }, { "title": "4 第1個(gè)夢(mèng) ", "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 最初之戰(zhàn)", "link": "http://www.17fa.com/files/article/html/90/90747/16548774.html " }]
獲取數(shù)據(jù)
有了 URLs 列表,接下來的工作就很機(jī)械了,遍歷 URLs 列表請(qǐng)求資源,獲取源碼,解析源碼,獲取小說,寫文件,但是,因?yàn)樽罱K將所有的章節(jié)保存入一個(gè)文件,要保證章節(jié)的順序,因此寫文件需要 同步操作,實(shí)際上,我在編碼的時(shí)候所有的操作都改成了同步方式
獲取源碼
通過解析讀取的 list.json
文件,獲取到 URLs 列表,遍歷列表獲取資源,因?yàn)樾枰_保章節(jié)的順序,所以這里引進(jìn) sync-request
模塊進(jìn)行同步 request 請(qǐng)求資源,請(qǐng)求資源后照例轉(zhuǎn)碼
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
模塊獲取小說內(nèi)容,避免影響觀感,寫操作之前去除內(nèi)容中的的 html 標(biāo)簽
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, '') }
保存小說
寫操作也需要同步操作,因此使用了同步寫函數(shù) fs.writeFileSync()
和 同步添加函數(shù) fs.appendFileSync()
,第一次寫使用寫函數(shù),之后的內(nèi)容都是進(jìn)行 append 操作,為了改善閱讀體驗(yàn),每個(gè)章節(jié)前添加標(biāo)題
也可以在內(nèi)容前添加 拍 [TOC],作為導(dǎo)航鏈接
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 + "鏈接出錯(cuò)!") }) } for (let i = 0; i < urlList.length; i++) { console.log(urlList[i]) getContent(urlList[i]) }
Markdown 轉(zhuǎn) PDF
我將小說保存在 Markdown 文件中,為了提升閱讀體驗(yàn),可以將 Markdown 文件轉(zhuǎn)換成 PDF 文件,目前我較為喜歡的兩種方式,通過 Chrome 的打印功能 以及 pandoc 轉(zhuǎn)換
Chrome 打印
SublimeText 有個(gè)插件 markdown preview
,可通過 Alt + m
快捷鍵在 Chrome 中預(yù)覽 Markdown,在 Chrome 頁面中右鍵,選擇打印,調(diào)整好參數(shù)后,選擇另存為 PDF,簡(jiǎn)單,粗暴,深得我心
打印效果:
pandoc 轉(zhuǎn)換
pandoc 是十分強(qiáng)大的文件格式轉(zhuǎn)換工具,可以將 Markdown 文件轉(zhuǎn)換成多種格式,今晚在 windows10 下折騰了半天,始終檢索不到 pdflatex,關(guān)于 pandoc,后面會(huì)專門寫一篇總結(jié)。
PDF 已經(jīng)發(fā)給老大了,現(xiàn)在正在看
關(guān)于python、node、爬蟲
在之前很長(zhǎng)的一段時(shí)間里,很想用 Python,很想寫爬蟲,更想用 Python 寫爬蟲,甚至成為了心里的一塊執(zhí)念,隨著接觸的知識(shí)更全面,執(zhí)念也逐漸淡去,少了很多“想”,遇事想著多去動(dòng)手,實(shí)踐出真知。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Node.js爬蟲如何獲取天氣和每日問候詳解
- node.js 基于cheerio的爬蟲工具的實(shí)現(xiàn)(需要登錄權(quán)限的爬蟲工具)
- node.js學(xué)習(xí)筆記之koa框架和簡(jiǎn)單爬蟲練習(xí)
- 手把手教你用Node.js爬蟲爬取網(wǎng)站數(shù)據(jù)的方法
- Node.js 利用cheerio制作簡(jiǎn)單的網(wǎng)頁爬蟲示例
- 淺談Node.js爬蟲之網(wǎng)頁請(qǐng)求模塊
- Node.js+jade+mongodb+mongoose實(shí)現(xiàn)爬蟲分離入庫與生成靜態(tài)文件的方法
- 基于node.js制作簡(jiǎn)單爬蟲教程
- 利用node.js寫一個(gè)爬取知乎妹紙圖的小爬蟲
- node.js實(shí)現(xiàn)博客小爬蟲的實(shí)例代碼
- 利用Node.js制作爬取大眾點(diǎn)評(píng)的爬蟲
- node.js爬蟲框架node-crawler初體驗(yàn)
相關(guān)文章
Node.js環(huán)境下Koa2添加travis ci持續(xù)集成工具的方法
這篇文章主要給大家介紹了在Node.js環(huán)境下Koa2添加travis ci持續(xù)集成工具的方法,文中介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面跟著小編一起來學(xué)習(xí)學(xué)習(xí)吧。2017-06-06利用Node.js了解與測(cè)量HTTP所花費(fèi)的時(shí)間詳解
這篇文章主要給大家介紹了關(guān)于利用Node.js了解與測(cè)量HTTP所花費(fèi)的時(shí)間的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-09-09nodejs如何在package.json中設(shè)置多條啟動(dòng)命令
這篇文章主要介紹了nodejs如何在package.json中設(shè)置多條啟動(dòng)命令,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03