这篇文章讲解一下yunshare项目的爬虫模型。

使用nodejs开发爬虫很简单,不需要类似python的scrapy这样的爬虫框架,只需要用request或者superagent这样的http库就能完成大部分的爬虫工作了。

使用nodejs开发爬虫半年左右了,爬虫可以很简单,也可以很复杂。简单的爬虫定向爬取一个网站,可能有个几万或者几十万的页面请求,复杂的爬虫类似google bot这样搜索引擎的蜘蛛爬虫,要每时每刻爬取互联网上最新的内容。

一般的个人开发者都是用爬虫定向爬取一些网站,然后提取一些结构化的数据,使用api接口获取数据也可以归到这一类。如果想简单的练习爬虫技术,可以尝试爬取豆瓣电影数据和书籍数据的,使用api接口和爬取html页面都能完成这个任务。

爬虫的说白了就是一个http客户端,通过http协议和远程http服务器通信,获取html页面内容或者其他的种子文件,pdf文件等等。和浏览器不同的一点就是爬虫不会把抓取的内容渲染出来,而是解析页面内容然后保存到数据库里面。

在开始学习爬虫的时候我考虑的是怎么爬取html页面内容,怎么解析html页面之间的链接规则,后来遇到了页面编码的问题。

统一utf8编码

国内网站主要是使用html和gbk这两种编码方式,解决编码有两种思路,第一个是在获取页面内容的时候根据页面的<meta charset='gbk'>编码把内容统一转码成utf8的,因为nodejs字符串默认编码就是utf8。

这个方案充满了不确定性。

问题1:不同网站的指定编码的方式不一样,除了前面提到的那种方式,还有<meta http-equiv="Content-Type" content="text/html; charset=gbk">这种方式指定编码,这个问题还不是很大,很多的http工具库都能正确的解析这两种编码,问题是还有很多网站没有指定编码,又或者指定的编码和文件的实际编码不一致(遇到过真实的案例)。

问题2:如果你把gbk编码的html文件转成utf8编码保存到本地,用浏览器直接打开这个文件的时候会显示乱码,非常不利于开发过程中的查找问题。

不转码html内容

既然前面的方案有这么多的问题,剩下的方法就是把html内容直接按照原来的编码保存到本地,然后解析的时候指定编码。

这个方法有2个好处:1、简化了爬虫模型,2、可以用浏览器打开html文件,不会乱码。唯一的缺点是不同网站文件内容解析的时候似乎需要指定编码,对于小规模爬虫这个问题其实影响不大。

统一爬虫模型

前面的编码方案解决了爬取不同网站html文件的编码问题,我们可以用一个统一的爬虫方法爬取不同网站的内容,那如果你想爬取非html内容呢?

是不是又要重新写一个爬虫方法,解决这个问题的方法就是http协议,假设我们写的这个爬虫方法就是一个完整的http客户端,那理论上这个客户端是不是能根据Content-Typ获取各种格式的文件。

那到底能不能用一个简单的方法就能实现上述的功能呢?下面的方法就是我采用request写的nodejs简单高效的爬虫模型。

function fetch(url) {
console.log(`down ${url} started`);
const deferred = Q.defer();
const file = getfile(url);
fs.ensureDirSync(path.dirname(file));
const stream = request
.get(url)
.on('error', (err) => {
deferred.reject(`down ${url}:${err}`);
})
.on('response', (res) => {
if (res.statusCode !== 200) {
deferred.reject(`down ${url}:${res.statusCode}`);
} else {
console.log(`down ${url}:${res.statusCode}`);
}
})
.pipe(fs.createWriteStream(`${file}`)); stream.on('finish', () => {
deferred.resolve();
});
return deferred.promise;
}

这段代码在yunshare/src/util/fetch.js里面,当然这个方法不能单独运行,但是关键的逻辑就是这么简单。

不管是什么格式的http请求,json,html,torrent等都统一把返回的二进制格式文件保存到以md5(url)为文件名的位置。上面的getfile就是用来获取文件路径的。

模型扩展

使用MD5散列还是有发生冲突的风险的,如果你想要爬取上亿的网页,可能还需要对上面的模型进行扩展。一个简单的思路就是把网页路径中的域名提取出来,不同网站的内容保存在对应的域文件夹下面。

其他的类似的思路也行,需要注意的就是如果爬虫保存文件和解析文件是分开的,你需要保证在解析文件的时候能用同样的方法定位这个文件。共同的参数就是url,所以你生成文件名的时候不能用一些随时间变化的参数。

最后,献上第一个使用node全栈开发的网站:哔哩搜索,目前索引百度网盘资源1000w条了。

简单高效的nodejs爬虫模型的更多相关文章

  1. 简单实现nodejs爬虫工具

    约30行代码实现一个简单nodejs爬虫工具,定时抓取网页数据. 使用npm模块 request---简单http请求客户端.(轻量级) fs---nodejs文件模块.   index.js var ...

  2. NodeJS 爬虫爬取LOL英雄联盟的英雄信息,批量下载英雄壁纸

    工欲善其事,必先利其器,会用各种模块非常重要. 1.模块使用 (1)superagent:Nodejs中的http请求库(每个语言都有无数个,java的okhttp,OC的afnetworking) ...

  3. Nodejs爬虫进阶教程之异步并发控制

    Nodejs爬虫进阶教程之异步并发控制 之前写了个现在看来很不完美的小爬虫,很多地方没有处理好,比如说在知乎点开一个问题的时候,它的所有回答并不是全部加载好了的,当你拉到回答的尾部时,点击加载更多,回 ...

  4. NodeJS爬虫系统初探

    NodeJS爬虫系统 NodeJS爬虫系统 0. 概论 爬虫是一种自动获取网页内容的程序.是搜索引擎的重要组成部分,因此搜索引擎优化很大程度上是针对爬虫而做出的优化. robots.txt是一个文本文 ...

  5. 【nodeJS爬虫】前端爬虫系列

    写这篇 blog 其实一开始我是拒绝的,因为爬虫爬的就是cnblog博客园.搞不好编辑看到了就把我的账号给封了:). 言归正传,前端同学可能向来对爬虫不是很感冒,觉得爬虫需要用偏后端的语言,诸如 ph ...

  6. nodejs爬虫如何设置动态ip以及userAgent

    nodejs爬虫如何设置动态ip以及userAgent 转https://blog.csdn.net/u014374031/article/details/78833765 前言 在写nodejs爬虫 ...

  7. nodejs爬虫--抓取CSDN某用户全部文章

    最近正在学习node.js,就像搞一些东西来玩玩,于是这个简单的爬虫就诞生了. 准备工作 node.js爬虫肯定要先安装node.js环境 创建一个文件夹 在该文件夹打开命令行,执行npm init初 ...

  8. NLP论文解读:无需模板且高效的语言微调模型(上)

    原创作者 | 苏菲 论文题目: Prompt-free and Efficient Language Model Fine-Tuning 论文作者: Rabeeh Karimi Mahabadi 论文 ...

  9. 【中文分词】简单高效的MMSeg

    最近碰到一个分词匹配需求--给定一个关键词表,作为自定义分词词典,用户query文本分词后,是否有词落入这个自定义词典中?现有的大多数Java系的分词方案基本都支持添加自定义词典,但是却不支持HDFS ...

随机推荐

  1. numpy函数fromfunction分析

    从函数规则创建数组是非常方便的方法.在numpy中我们常用fromfunction函数来实现这个功能. 在numpy的官网有这么一个例子. >>> def f(x,y): ... r ...

  2. JavaScript(七)——视频插入

    代码: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3. ...

  3. python中set集合

    一.set集合的特性 访问速度快 天生解决重复问题 二.set变量申明 s1 = set() s2 = set([1,2,3]) 备注:第二种方式在set类中直接传入一个序列. 三.set类中方法大全 ...

  4. mysql优化---订单查询优化:视图优化+索引创建

    订单的表结构采用了垂直分表的策略,将订单相关的不同模块的字段维护在不同表中 在订单处理这个页面,需要查询各种维度, 因此为了方便查询创建了v_sale_order视图(老版本) drop view v ...

  5. RabbitMQ与Redis队列对比

    本文仅针对RabbitMQ与Redis做队列应用时的情况进行对比具体采用什么方式实现,还需要取决于系统的实际需求 简要介绍 RabbitMQ RabbitMQ是实现AMQP(高级消息队列协议)的消息中 ...

  6. ELF Format 笔记(二)—— ELF Header

    ilocker:关注 Android 安全(新入行,0基础) QQ: 2597294287 以 32 位的 ELF header 数据结构为例: #define EI_NIDENT 16 typede ...

  7. openwrt修改flash大小

    前言 默认openwrt trunk编译出来的flash大小为8M,但是我们手上的板子可能flash大小更大,本文以MT7620a为例,将其flash大小由8M修改为16M或者32M 增加dts文件 ...

  8. Linux vi 操作命令整理

    转自:http://www.lupaworld.com/?uid-296380-action-viewspace-itemid-118973   vi/vim 基本使用方法 本文介绍了vi (vim) ...

  9. C#基础---IComparable用法,实现List<T>.sort()排序

    List<T>.sort()可以实现对T的排序,比如List<int>.sort()执行后集合会按照int从小到大排序.如果T是一个自定义的Object,可是我们想按照自己的方 ...

  10. 解决android中Layout文件下的xml文件配好后,R类中不能自动生成相应代码

    不能更新的原因: 1.在xml文件中代码错误或者格式错误 2.eclipse 编译器是老版本 3.布局文件的文件名有大写字母 4.含有相同文件名.格式的xml文件 解决方法: 1.找到出错的xml文件 ...