首页
学习
活动
专区
工具
TVP
发布

精通 Node 爬虫-03-知乎专栏爬虫实战

使用到的模块

由于知乎专栏现在使用的是API,抓取直接请求API就好,获取到数据,把数据转成markdown格式存在本地,方便以后看。所以使用的模块就三个。

lodash

mkdirp

request

turndown

抓取原理

在编写代码前,需要先对整个逻辑有个大概的思路:

1. 使用请求知乎专栏的数据

2. 使用把html内容转为markdown格式

3. 保存为本地文件

原理是很简单,但是实际操作起来并没有这么容易。在写代码的过程中,就会发现各种各样的问题:

1. 知乎专栏的api每次只能获取每次20条的数据。

2. 处理代码块的标签嵌套和知乎API返回的不一样。

3. 要是保存到一个并不存在的文件夹下,NodeJS的模块并不支持自动创建文件夹。

基于这几个问题,我们需要另外写对应的方法来解决问题。

1. 编写对应的方法,输入知乎专栏的文章数量,返回一个包含所有文章URL的数组,遍历这个数组即可得到获取到所有的文章。

2. 使用正则把代码块的标签嵌套替换成能识别的格式,方便得到我们想要的MarkDown格式的文档。

3. 文件路径不存在就创建路径。

经过合理的封装,最顶部的代码就5行:

async function zhuanlan(postID, localPath = './') {

console.log(`----- $ start -----`);

mkdir(path.resolve(localPath, postID));

markdown(localPath, postID, await Posts(postID));

};

实际上正真起作用的代码就两行,创建文件夹,将专栏API返回的所有数据转成markdown,并储存。

代码解析

上面的代码可以分成三个大模块,分别是,和。

mkdir 创建文件夹

因为fs的mkdir方法只能创建一层的文件夹,如果想创建更深层的文件夹,还是使用已经封装好的模块。

function mkdir(filePath) {

if (fs.existsSync(`$`)) {

console.log(` $ 文件夹已经存在`);

} else {

mkdirp(`$`, (err) = {

if (err) {

console.error(err);

} else {

console.log(` 创建 $文件夹成功`);

}

});

}

}

文件夹是存放markdown文档的基础,只有先创建好文件夹,才能保证爬虫抓取的数据有用处。要不然爬了也是白爬取。

Posts 抓取逻辑

为什么这个方法叫,因为知乎的专栏文章的API就使用这个为路径。

在这个方法里,实现了将所有的文章API的URL处理成数组,并循环抓取所有的文章数据。

获取专栏的信息,专栏信息中有专栏文章的数量值,需要先获取到这个信息才能知道循环请求何时停止。使用lodash的模板方法,得到url,因为返回的数据是压缩过的,需要request解压,所以要设置。

const zhuanlanInfo = async (columnsID) = {

const urlTemplate = _.template(API.post.columns)({ columnsID });

let object = {};

object = {

url: urlTemplate,

gzip: true,

};

return JSON.parse((await request(object)).body);

}

将文章数量转换为循环次数。每次访问20条数据,减少请求的数量,节省时间的同时,还能减少对知乎服务器的负担,没毛病。(这里没写延时函数,如果持续抓取,建议加个延时函数)。

let rateMethod = (count, cycle) = {

count = count === undefined ? 20 : count;

cycle = cycleMethod(cycle);

let posts = count % cycle;

let times = (count - posts) / cycle;

return {

times,

count,

cycle,

writeTimes: 0,

allObject: {}

}

}

let cycleMethod = (cycle) = {

let defaultCycle = 20;

if (cycle cycle !== defaultCycle) {

cycle = cycle % defaultCycle;

}

cycle = cycle || defaultCycle;

return cycle;

}

这里得到的循环次数是从0开始计算的。

循环函数获取所有文章

let loopMethod = (config, callback) = {

let { urlTemplate, ...options } = config.options;

let opts = {

url: url.resolve(urlTemplate, `?limit=$offset=$`),

...options

}

request(opts).then((c) = {

c = JSON.parse(c.body);

forEach(c, (item, index) = {

config.allObject[index + config.writeTimes * 20] = item;

});

if (config.writeTimes === config.times) {

callback(config.allObject);

} else {

config.writeTimes += 1;

loopMethod(config, callback);

}

});

}

这里使用的是callback,当然也可以写成Promise,谁写成Promise记得给我PR一下哈。

抓取过程到这里就完了。下面是数据处理的工作了。

MarkDown 数据处理与储存

获取到数据,最后需要保存成MarkDown的格式。这个模块处理的事情就是将所有的数据转换成MarkDown语法格式,保存到本地。

这一块的代码还是存在一些bug,在windows系统下,文件命名不能存在特殊字符,

食用方法

emmmmmm......自己看README吧。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20190829A09FYV00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券