俺尋思這node版的漫畫爬蟲沒人要捏...

Emine發表於2018-08-25

一直垂涎網路爬蟲程式,但是俺只是一個渣渣前端,對爬蟲真是難以下手啊,當第一次聽到了node這奇葩可以用js去寫後端的時候,就想著能不能用node去實現爬蟲呢?知道可以的我,就像20幾年的俺頭一次看見了黃花大閨女一般,情難自抑~ 於是乎我本人:

幹勁 +100

興趣 +100

但是啊!!關鍵是

不懶惰 +0

懶屬性一點依然“皮實”,所以遲遲也沒有開始上手去做爬蟲...

直到!上週看海賊王不過癮,決定看漫畫,但是網路不好啊,看的鬧心,於是就終於痛定思痛,寫下了 let http = require('http');

昨天終於寫完了,就分享給對這個感興趣的小夥伴吧~

let http = require('http');
let colors = require('colors');
let fs = require('fs');
let cheerio = require('cheerio');

const INIT = {
    pagePath: '/02/Vol_001/index_0.html',
    mounts: 100000
}

if(process.argv.length !== 2) {
    let begin, mounts;
    process.argv.forEach(item => {
        if (item.includes('begin')) {
            begin = item.split('=')[1]
        }
        if (item.includes('mounts')) {
            mounts = item.split('=')[1]
        }
    })
    INIT.pagePath = begin ? String(begin) :  INIT.pagePath;
    INIT.mounts = mounts ? Number(mounts) :  INIT.mounts;
}

// 初始頁資訊
let pageInfo = {
    hostname: 'manhua.fzdm.com',
    path: INIT.pagePath
}

// 初始圖片資訊
let imgInfo = {
    hostname: 'p0.xiaoshidi.net',
    path: ``
}

/**
 * @description 請求當前頁面資訊
 * @param {Object} pageInfo - 初始頁資訊
 * @param {string} pageInfo.hostname - 頁域名地址
 * @param {string} pageInfo.path - 路徑
*/
let getPageInfo = (address) => {
    let client = http.request({
        hostname: address.hostname,
        path: address.path
    }, (res) => {
        let contents = '';
        res.on('data', (callbackData) => {
            console.log('=======響應開始'.red);
            contents += callbackData;
        })
        res.on('end', () => {
            let $ = cheerio.load(contents);
            let sContentsInfo = (/<script type="text\/javascript">(.+?Title.+?)<\/script>/).exec(contents)[1];
            // 獲取當頁圖片的地址
            let mhurl = (/mhurl="(.+?)\.jpg\"/).exec(sContentsInfo);
            imgInfo.path = '/' + mhurl[1] + '.jpg'; console.log(imgInfo.path)
            lineDetail(imgInfo.path)

            // 獲取下一頁地址
            let nextPage = $('.navigation').children().last().prop('href');
            pageInfo.path = `/${(/Clid="(.+?)\"/).exec(sContentsInfo)[1]}\/${(/Url="(.+?)\"/).exec(sContentsInfo)[1]}/${nextPage}`; console.log(pageInfo.path)

            // 圖片名字生成
            let imgName = mhurl[1].replace(/\//g,'_').replace(/\d+\_/,'').replace(/[a-zA-Z]+/,'');
            console.log(imgName.cyan);

            // 儲存圖片
            downloadImg(imgInfo, imgName)
            console.log('=======響應結束'.red);
        })
    })
    client.end();
    client.on('error', (err) => {
        console.log(err);
    })
}

/**
 * @description 圖片質量線路檢測切換(from source code)
 * @param {string} mhurl - 圖片地址
 * @param {string} mhurl - 線路
 * @returns {string} 返回優質圖片地址
*/
function lineDetail(mhurl, mhss = 'p1.xiaoshidi.net') {
    let mhpicurl = '';
    if (mhurl.indexOf("2015") != -1 || mhurl.indexOf("2016") != -1 || mhurl.indexOf("2017") != -1 || mhurl.indexOf("2018") != -1) {

    } else {
        mhss = mhss.replace(/p1/, "p0").replace(/p2/, "p0").replace(/p07/, "p17")
    }
    mhpicurl = mhss + "" + mhurl;
    if (mhurl.indexOf("http") != -1) { 
        fs.writeFile(`./one-piece/${mhurl.split('://')[1]}.text`, `${mhurl}下的圖片無法抓取,請手動檢視吧~`, (err) => {
            console.log(err);
        })
        mhpicurl = 'p17.xiaoshidi.net/2/Vol_001/001aa.jpg'
    }
    return mhpicurl;
}

/**
 *  @desc  下載圖片並儲存 
 *  @param { any } info  圖片地址資訊
 *  @param { string } name  圖片儲存的名字
*/
function downloadImg(info, name) {
    let client = http.request({
        hostname: info.hostname,
        path: info.path
    },(res) => {
        res.setEncoding('binary');
        let contents = '';

        res.on('data', (backData) => {
            contents += backData;
        })

        res.on('end', () => {
            // 沒有 one-piece 資料夾則新建一個
            fs.access('./one-piece', (err) => {
                if(err) fs.mkdirSync('./one-piece');

                fs.writeFile(`./one-piece/${name}.jpg`, contents, 'binary', (err) => {
                    if(err) throw err;
                    console.log('圖片已儲存'.green);
                })
            })
            
        })
    })
    client.end();
    client.on('error', (e) => {
        console.log(e)
    })
}

// 執行爬蟲
let i = 1;
let t = setInterval(() => {
    if (i >= INIT.mounts) clearInterval(t);
    getPageInfo(pageInfo);
    i++;
},1300)

複製程式碼

本人的註釋還是寫得比較完備的,有什麼不明白的小夥伴可以我們們探討探討。

附上github地址,使用方法在內有寫 github.com/Luooojunnn/… 喜歡的小夥伴請給俺人生中第一個star好嗎?俺會持續更新一些有意思的爬蟲~

相關文章