Node.js 實現簡單小說爬蟲


最近因為劇荒,老大追了愛奇藝的一部網劇,由丁墨的同名小說《美人為餡》改編,目前已經放出兩季,雖然整部劇槽點滿滿,但是老大看得不亦樂乎,並且在看完第二季之后跟我要小說資源,直接要奔原著去看結局……
隨手搜了下,都是在線資源,下載的話需要登錄,注冊登錄好麻煩,寫個爬蟲玩玩也好,於是動手用 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(/\&nbsp;/g, '')
}

保存小說

寫操作也需要同步操作,因此使用了同步寫函數 fs.writeFileSync() 和 同步添加函數 fs.appendFileSync(),第一次寫使用寫函數,之后的內容都是進行 append 操作,為了改善閱讀體驗,每個章節前添加標題
**也可以在內容前添加 拍

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(/\&nbsp;/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,簡單,粗暴,深得我心
打印效果:

pandoc 轉換

pandoc 是十分強大的文件格式轉換工具,可以將 Markdown 文件轉換成多種格式,今晚在 windows10 下折騰了半天,始終檢索不到 pdflatex,關於 pandoc,后面會專門寫一篇總結。

PDF 已經發給老大了,現在正在看

關於python、node、爬蟲

在之前很長的一段時間里,很想用 Python,很想寫爬蟲,更想用 Python 寫爬蟲,甚至成為了心里的一塊執念,隨着接觸的知識更全面,執念也逐漸淡去,少了很多“想”,遇事想着多去動手,實踐出真知。


talk is cheap, show me your code

轉自個人站點:http://lijundong.com/novel-crawler-by-Nodejs/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM