【JavaScript】一个基于node.js的小说爬取及上传器

1,058 阅读3分钟

代码由来

今天整理硬盘的时候发现了初三时候帮班里一个人写的小程序。它的功能就是从小说网页上按要求抓取小说文本并批量上传到我们初中时候的网络学习平台上笔记本模块里,以便这位同学可以在学校里愉快地享受。

实现思路

这个小程序大概的思路就是借助node.js自带的http模块批量请求小说网站的html代码并利用正则表达式剔除不必要的内容并对文本作必要修正,之后再利用https模块,将事先设定好的cookie写入请求,调用学习平台的api接口,将小说文本批量发送到平台上。

涉及技术

本程序主要涉及了async/await函数、Promise、正则表达式、模板字符串、http请求、node.js模块等有关JavaScript的基础知识点,对js新手还是有一定的参考价值。由于这个程序的时间已经有点儿旧了,当时的代码必越显粗糙、有不足之处,还请各位大佬批评指正。

完整代码分享

"use strict";
const User = {
    service: 'www.tycqxs.com',
    charset: 'utf-8',
    currentPath: '18415171',  //当前需要获取的小说地址,请初始化为你需要获取的第一篇小说的地址
    reqTime:  100,  //请设置你要获取多少次小说
    novelName: '7_7505',  //小说名称代码
    novels:  [],  //存放获取到的小说,请勿乱动!!!
    lekeCookie: "Hm_lvt_1cc429d7f39859f7f65470f59284e944=1558167567,1558174726; ticket=VFdwUlBRPT07SUMwdEtTQW9MU290S3lrPTsyNDI0; _nk_=%E9%99%88%E6%88%90; _hb=1; Hm_lpvt_1cc429d7f39859f7f65470f59284e944=1558174760",  //配置你的cookie
};
const MyHttp = {
    _api: {
        'http': require('http'),
        'https': require('https'),
    },
    iconv: require('iconv-lite'),
    querystring: require('querystring'),
    get(path, hostname, api = 'https') {
        let json = {
            'hostname': hostname,
            'path': path,
            'method': 'GET',
            'User-Agent': "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36",
        };
        return new Promise(function(success, fail) {
            const req = MyHttp._api[api].request(json, (res)=>{
                let arrBuf = [],
                     bufLength = 0;
                res.on('data', chunk=>{
                    arrBuf.push(chunk);
                    bufLength += chunk.length;
                });
                res.on('end', _=>{
                    let chunkAll = Buffer.concat(arrBuf, bufLength),
                         str = MyHttp.iconv.decode(chunkAll, User.charset);  //汉字不乱码
                    success(str);
                });
                res.on('error', e=>fail(e));
            });
            req.end();
        });
    },
    post(path, hostname, data, api = 'https') {
        let strData = MyHttp.querystring.stringify(data);
        let json = {
            'hostname': hostname,
            'path': path,
            'method': 'POST',
            'headers': {
                'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
                'Content-Length': strData.length,
                'Cookie': User.lekeCookie,  //带上cookie才能访问乐课的接口
            },
        };
        return new Promise(function(success, fail) {
            const req = MyHttp._api[api].request(json, (res)=>{
                res.on('data', _=>{  //post请求成功需要监听data事件
                    success(res);
                });
                res.on('error', e=>fail(e));
            });
            req.write(strData);  //写入请求内容
            req.end();
        });
    },
};
async function getNovel() {
    if(/^[0-9]+$/.test(User.currentPath)) {  //检查有没有上传到大结局
        const html = await MyHttp.get(`/${User.novelName}/${User.currentPath}.html`, User.service, 'http');  //异步获取小说资源
        let title;
        User.currentPath = html.match(/<a href="\/([\s\S]+?)\/([0-9]+).html">\u4e0b\u4e00\u7ae0<\/a>/)[2];  //更新currentPath
        User.reqTime--;  //更新reqTime
        User.novels.push({  //保存获取到的小说
            'title': (title = html.match(/<h1>([\s\S]+?)<\/h1>/)[1]),  //小说标题
            'text': html.match(/<div id="content"([\s\S]+?)>([\s\S]+?)<\/div>/)[2].replace(/&nbsp;/g, '\u0020'),  //修正空格、换行
        });
        console.log(`成功获取${title}`);
    } else {
        throw `User.currentPath异常!也许已经上传到大结局啦!`;
    }
};
async function postNovel(obj) {
    await MyHttp.post('/auth/pc/note/ajax/addExtraNote.htm', 'note.leke.cn', {  //直接调用乐课接口上传小说
        'noteBookId': '',
        'title': obj.title,
        'content': obj.text,
        'description': obj.text,
        'audioUrl': '',
        'isKey': '0',
    });
};
/* 主线程逻辑开始 */
(async function init() {
    //step1:获取所有小说并存入Uesr.novels中
    while(User.reqTime) {  //如果reqTime还没用光则递归,直到获取所有小说为止
        await getNovel();
    }
    console.log(`step1完成!共获取小说${User.novels.length}篇`);
    //step2:把获取到的小说传至云端
    for(let obj of User.novels) {
        console.log(`正在发送《${obj.title}》...`);
        await postNovel(obj);
        console.log(`《${obj.title}》发送成功!`);
    }
    console.log('step2完成!');
})();

注:相关网站的升级以上代码可能无法正常运行,贴出代码主要是为了分享实现思路,请勿将本代码用于任何不良用途~