爬取数据后使用哪个数据库存储数据的,为什么?

MongoDB 是使用比较多的数据库,这里以 MongoDB 为例,大家需要结合自己真实开发环境回答。

原因:

1.与关系型数据库相比,MongoDB 的优点如下。

(1) 弱一致性(最终一致),更能保证用户的访问速度

举例来说,在传统的关系型数据库中,一个 COUNT 类型的操作会锁定数据集,这样可以保证得到“当前”情况下的较精确值。这在某些情况下,例 如通过 ATM 查看账户信息的时候很重要,但对于 Wordnik 来说,数据是不断更新和增长的,这种“较精确”的保证几乎没有任何意义,反而会产生很大的延 迟。他们需要的是一个“大约”的数字以及更快的处理速度。

但某些情况下 MongoDB 会锁住数据库。如果此时正有数百个请求,则它们会堆积起来,造成许多问题。我们使用了下面的优化方式来避免锁定。

每次更新前,我们会先查询记录。查询操作会将对象放入内存,于是更新则会尽可能的迅速。在主/从部署方案中,从节点可以使用“-pretouch”参数运行,这也可以得到相同的效果。

使用多个 mongod 进程。我们根据访问模式将数据库拆分成多个进程。

(2) 文档结构的存储方式,能够更便捷的获取数据。

对于一个层级式的数据结构来说,如果要将这样的数据使用扁平式的,表状的结构来保存数据,这无论是在查询还是获取数据时都十分困难。

(3) 内置 GridFS,支持大容量的存储。

GridFS 是一个出色的分布式文件系统,可以支持海量的数据存储。内置了 GridFS 了 MongoDB,能够满足对大数据集的快速范围查询。

(4) 内置 Sharding。

提供基于 Range 的 Auto Sharding 机制:一个 collection 可按照记录的范围,分成若干个段,切分到不同的Shard上。Shards可以和复制结合,配合Replica sets能够实现Sharding+fail-over,不同的 Shard 之间可以负载均衡。查询是对 客户端是透明的。客户端执行查询,统计,MapReduce 等操作,这些会被 MongoDB 自动路由到后端的数据节点。这让我们关注于自己的业务,适当的时候可以无痛的升级。MongoDB 的 Sharding 设计能力较大可支持约 20 petabytes,足以支撑一般应用。

这可以保证 MongoDB 运行在便宜的 PC 服务器集群上。PC 集群扩充起来非常方便并且成本很低,避免了“sharding”操作的复杂性和成本。

(5) 第三方支持丰富。(这是与其他的 NoSQL 相比,MongoDB 也具有的优势)

现在网络上的很多 NoSQL 开源数据库完全属于社区型的,没有官方支持,给使用者带来了很大的风险。而开源文档数据库 MongoDB 背后有商业公司 10gen 为其提供供商业培训和支持。而且 MongoDB 社区非常活跃,很多开发框架都迅速提供了对 MongDB 的支持。不少知名大公司和网站也在生产环境中使用MongoDB,越来越多的创新型企业转而使用 MongoDB 作为和 Django,RoR 来搭配的技术方案。

(6) 性能优越

在使用场合下,千万级别的文档对象,近 10G 的数据,对有索引的 ID 的查询不会比 mysql 慢,而对非索引字段的查询,则是全面胜出。 mysql 实际无法胜任大数据量下任意字段的查询,而 mongodb 的查询性能实在让我惊讶。写入性能同样很令人满意,同样写入百万级别的数据,mongodb 比我以前试用过的couchdb 要快得多,基本 10 分钟以下可以解决。补上一句,观察过程中 mongodb 都远算不上是 CPU 杀手。

2.Mongodb 与 redis 相比较

(1) mongodb 文件存储是 BSON 格式类似 JSON,或自定义的二进制格式。mongodb 与 redis 性能都很依赖内存的大小,mongodb 有丰富的数据表达、索引;最类似于关系数据库,支持丰富的查询语言,redis 数据丰富,较少的 IO ,这方面 mongodb 优势明显。

(2) mongodb 不支持事物,靠客户端自身保证,redis 支持事物,比较弱,仅能保证事物中的操作按顺序执行,这方面 redis 优于 mongodb。

(3) mongodb 对海量数据的访问效率提升,redis 较小数据量的性能及运算,这方面 mongodb 性能优于 redis.monbgodb 有mapredurce功能,提供数据分析,redis 没有,这方面 mongodb 优于 redis。

你用过的爬虫框架或者模块有哪些?谈谈他们的区别或者优缺点?

用过的:

(1) Python 自带:urllib、urllib2

urllib 和 urllib2 模块都做与请求 URL 相关的操作,但他们提供不同的功能。

urllib2:urllib2.urlopen 可以接受一个 Request 对象或者 url,(在接受 Request 对象时候,并以此可以来设置一个 URL 的 headers),urllib.urlopen 只接收一个 url。

urllib 有 urlencode,urllib2 没有,因此总是 urllib,urllib2 常会一起使用的原因。

(2) 第三方:requests

request 是一个 HTTP 库, 它只是用来,进行请求,对于 HTTP 请求,他是一个强大的库,下载,解析全部自己处理,灵活性更高,高并发与分布式部署也非常灵活,对于功能可以更好实现

(3) 框架: Scrapy

scrapy 是封装起来的框架,它包含了下载器,解析器,日志及异常处理,基于多线程,twisted 的方式处理,对于固定单个网站的爬取开发,有优势,但是对于多网站爬取,并发及分布式处理方面,不够灵活,不便调整与括展。

Scrapy 优点

scrapy 是异步的

采取可读性更强的 xpath 代替正则

强大的统计和 log 系统

同时在不同的 url 上爬行

支持 shell 方式,方便独立调试

写 middleware,方便写一些统一的过滤器

通过管道的方式存入数据库

Scrapy 缺点

基于 python 的爬虫框架,扩展性比较差

基于 twisted 框架,运行中的 exception 是不会干掉 reactor,并且异步框架出错后是不会停掉其他任务的,数据出错后难以察觉。

写爬虫是用多进程好?还是多线程好? 为什么?

IO 密集型代码(文件处理、网络爬虫等),多线程能够有效提升效率(单线程下有 IO 操作会进行 IO 等待,造成不必要的时间浪费,而开启多线程能在线程 A 等待时,自动切换到线程 B,可以不浪费 CPU 的资源,从而能提升程序执行效率)。在实际的数据采集过程中,既考虑网速和响应的问题,也需要考虑自身机器的硬件情况,来设置多进程或多线程。

常见的反爬虫和应对方法?

(1) 通过 Headers 反爬虫

从用户请求的 Headers 反爬虫是最常见的反爬虫策略。很多网站都会对 Headers 的 User-Agent 进行检测,还有一部分网站会对 Referer 进行检测(一些资源网站的防盗链就是检测 Referer)。如果遇到了这类反爬虫机制,可以直接在爬虫中添加 Headers,将浏览器的 User-Agent 复制到爬虫的 Headers 中;或者将 Referer 值修改为目标网站域名。对于检测 Headers 的反爬虫,在爬虫中修改或者添加 Headers 就能很好的绕过。

(2) 基于用户行为反爬虫

还有一部分网站是通过检测用户行为,例如同一 IP 短时间内多次访问同一页面,或者同一账户短时间内多次进行相同操作。

大多数网站都是前一种情况,对于这种情况,使用 IP 代理就可以解决。可以专门写一个爬虫,爬取网上公开的代理 ip,检测后全部保存起来。这样的代理 ip 爬虫经常会用到,最好自己准备一个。有了大量代理 ip 后可以每请求几次更换一个 ip,这在 requests 或者 urllib2 中很容易做到,这样就能很容易的绕过第一种反爬虫。

对于第二种情况,可以在每次请求后随机间隔几秒再进行下一次请求。有些有逻辑漏洞的网站,可以通过请求几次,退出登录,重新登录,继续请求来绕过同一账号短时间内不能多次进行相同请求的限制。

(3) 动态页面的反爬虫

上述的几种情况大多都是出现在静态页面,还有一部分网站,我们需要爬取的数据是通过 ajax 请求得到,或者通过 JavaScript 生成的。首先用 Fiddler 对网络请求进行分析。如果能够找到 ajax 请求,也能分析出具体的参数和响应的具体含义,我们就能采用上面的方法,直接利用 requests 或者 urllib2模拟 ajax 请求,对响应的 json 进行分析得到需要的数据。

能够直接模拟 ajax 请求获取数据固然是极好的,但是有些网站把 ajax 请求的所有参数全部加密了。我们根本没办法构造自己所需要的数据的请求。这种情况下就用 selenium+phantomJS,调用浏览器内核,并利用 phantomJS 执行 js 来模拟人为操作以及触发页面中的 js 脚本。从填写表单到点击按钮再到滚动页面,全部都可以模拟,不考虑具体的请求和响应过程,只是完完整整的把人浏览页面获取数据的过程模拟一遍。

用这套框架几乎能绕过大多数的反爬虫,因为它不是在伪装成浏览器来获取数据(上述的通过添加Headers 一定程度上就是为了伪装成浏览器),它本身就是浏览器,phantomJS 就是一个没有界面的浏览器,只是操控这个浏览器的不是人。利 selenium+phantomJS 能干很多事情,例如识别点触式(12306)或者滑动式的验证码,对页面表单进行暴力破解等。

需要登录的网页,如何解决同时限制 ip,cookie,session(其中有一些是动态生成的)在不使用动态爬取的情况下?

(1) 解决限制 IP 可以使用代理 IP 地址池、服务器

(2)不适用动态爬取的情况下可以使用反编译 JS 文件获取相应的文件,或者换用其他平台(比如手机端)看看是否可以获取相应的 json 文件。

验证码的解决?

(1) 图形验证码

干扰、杂色不是特别多的图片可以使用开源库 Tesseract 进行识别,太过复杂的需要借助第三方打码平台。

(2) 滑块验证码

点击和拖动滑块验证码可以借助 selenium、无图形界面浏览器(chromedirver 或者 phantomjs)和pillow 包来模拟人的点击和滑动操作,pillow 可以根据色差识别需要滑动的位置。

使用最多的数据库(Mysql,Mongodb,redis等),对他们的理解?

(1) MySQL 数据库

开源免费的关系型数据库,需要实现创建数据库、数据表和表的字段,表与表之间可以进行关联(一对多、多对多),是持久化存储。

(2) Mongodb 数据库

非关系型数据库,数据库的三元素是,数据库、集合、文档,可以进行持久化存储,也可作为内存数据库,存储数据不需要事先设定格式,数据以键值对的形式存储。

(3) redis 数据库

非关系型数据库,使用前可以不用设置格式,以键值对的方式保存,文件格式相对自由,主要用与缓存数据库,也可以进行持久化存储。

写一个邮箱地址的正则表达式?

[A-Za-z0-9_-]+@[a-zA-Z0-9_-]+(.[a-zA-Z0-9_-]+)+$

如何定时启动你的爬虫项目?

(1) 最简单的方法:直接使用 Timer 类

1. import time
2. import os
3.
4. while True:
5. os.system("scrapy crawl News")
6. time.sleep(86400) #每隔一天运行一次 24*60*60=86400s

(2) 使用 sched

1. import sched
2. #初始化 sched 模块的 scheduler 类
3. #第一个参数是一个可以返回时间戳的函数,第二个参数可以在定时未到达之前阻塞。
4. schedule = sched.scheduler ( time.time, time.sleep )
5.
6. #被周期性调度触发的函数
7. def func():
8. os.system("scrapy crawl News")
9. def perform1(inc):
10. schedule.enter(inc,0,perform1,(inc,))
11. func() # 需要周期执行的函数
12. def mymain():
13. schedule.enter(0,0,perform1,(86400,))
14.
15. if __name__=="__main__":
16. mymain()
17. schedule.run() # 开始运行,直到计划时间队列变成空为止

(3) 使用 Crontab

爬的那些内容数据量有多大,多久爬一次,爬下来的数据是怎么存储?

京东整站的数据大约在 1 亿左右,爬下来的数据存入数据库,mysql 数据库中如果有重复的 url 建议去重存入数据库,可以考虑引用外键。评分、评论如果做增量,Redis 中 url 去重,评分和评论建议建立一张新表用 id 做关联。

多久爬一次这个问题要根据公司的要求去处理,不一定是每天都爬。

Mongo 建立唯一索引键(id)可以做数据重复 前提是数据量不大, 2 台电脑几百万的情况 数据库需要做分片 (数据库要设计合理)。

例:租房的网站数据量每天大概是几十万条 ,每周固定爬取。

cookie 过期的处理问题?

因为 cookie 存在过期的现象,一个很好的处理方法就是做一个异常类,如果有异常的话 cookie 抛出异常类在执行程序。

动态加载又对及时性要求很高怎么处理?

Selenium + Phantomjs,尽量不使用 sleep 而使用 WebDriverWait

谈一谈你对 Selenium 和 PhantomJs了解?

Selenium 是一个 Web 的自动化测试工具,可以根据我们的指令,让浏览器自动加载页面,获取需要的数据,甚至页面截屏,或者判断网站上某些动作是否发生。Selenium 自己不带浏览器,不支持浏览器的功能,它需要与第三方浏览器结合在一起才能使用。但是我们有时候需要让它内嵌在代码中运行,所以我们可以用一个叫 PhantomJS 的工具代替真实的浏览器。Selenium 库里有个叫 WebDriver 的 API。WebDriver 有点儿像可以加载网站的浏览器,但是它也可以像 BeautifulSoup 或者其他 Selector 对象一样用来查找页面元素,与页面上的元素进行交互 (发送文本、点击等),以及执行其他动作来运行网络爬虫。

PhantomJS 是一个基于 Webkit 的“无界面”(headless)浏览器,它会把网站加载到内存并执行页面上的 JavaScript,因为不会展示图形界面,所以运行起来比完整的浏览器要高效。相比传统的 Chrome 或 Firefox 浏览器等,资源消耗会更少。

如果我们把 Selenium 和 PhantomJS 结合在一起,就可以运行一个非常强大的网络爬虫了,这个爬虫可以处理 JavaScript、Cookie、headers,以及任何我们真实用户需要做的事情。

主程序退出后,selenium 不保证 phantomJS 也成功退出,最好手动关闭 phantomJS 进程。(有可能会导致多个 phantomJS 进程运行,占用内存)。

WebDriverWait 虽然可能会减少延时,但是目前存在 bug(各种报错),这种情况可以采用 sleep。phantomJS 爬数据比较慢,可以选择多线程。如果运行的时候发现有的可以运行,有的不能,可以尝试将 phantomJS 改成 Chrome。

代理 IP 里的“透明”“匿名”“高匿”分别是指?

(1) 透明代理

它的意思是客户端根本不需要知道有代理服务器的存在,但是它传送的仍然是真实的 IP。你要想隐藏的话,不要用这个。

(2) 普通匿名代理

普通匿名代理能隐藏客户机的真实 IP,但会改变我们的请求信息,服务器端有可能会认为我们使用了代理。不过使用此种代理时,虽然被访问的网站不能知道你的 ip 地址,但仍然可以知道你在使用代理,当然某些能够侦测 ip 的网页仍然可以查到你的 ip。

(3) 高匿名代理

高匿名代理不改变客户机的请求,这样在服务器看来就像有个真正的客户浏览器在访问它,这时客户的真实 IP 是隐藏的,服务器端不会认为我们使用了代理。

设置代理有以下两个好处

让服务器以为不是同一个客户端在请求
防止我们的真实地址被泄露,防止被追究

robots 协议有什么用处?

Robots 协议:网站通过 Robots 协议告诉搜索引擎哪些页面可以抓取,哪些页面不能抓取。

为什么 requests 请求需要带上 header?

原因是:模拟浏览器,欺骗服务器,获取和浏览器一致的内容

header 的形式:字典
headers = {“User-Agent”: “Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36(KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36”}
用法: requests.get(url,headers=headers)

dumps,loads 与 dump,load 的区别?

json.dumps() 将 pyhton 的 dict 数据类型编码为 json 字符串

json.loads() 将 json 字符串解码为 dict 的数据类型

json.dump(x,y) x 是 json 对象, y 是文件对象,最终是将 json 对象写入到文件中

json.load(y) 从文件对象 y 中读取 json 对象

平常怎么使用代理的?

(1) 自己维护代理池

(2) 付费购买(目前市场上有很多 ip 代理商,可自行百度了解,建议看看他们的接口文档(API&SDK))

IP 存放在哪里?怎么维护 IP?对于封了多个 ip的,怎么判定 IP 没被封?

存放在数据库(redis、mysql 等)

维护多个代理网站

一般代理的存活时间往往在十几分钟左右,定时任务,加上代理 IP 去访问网页,验证其是否可用,如果返回状态为 200,表示这个代理是可以使用的。

怎么获取加密的数据?

(1) Web 端加密可尝试移动端(app)

(2) 解析加密,看能否破解

(3) 反爬手段层出不穷,js 加密较多,只能具体问题具体分析

假如每天爬取量在 5、6 万条数据,一般开几个线程,每个线程 ip 需要加锁限定吗

(1) 5、6 万条数据相对来说数据量比较小,线程数量不做强制要求(做除法得一个合理值即可)

(2) 多线程使用代理,应保证不在同时一刻使用一个代理 IP

怎么监控爬虫的状态?

(1) 使用 python 的 STMP 包将爬虫的状态信心发送到指定的邮箱

(2) Scrapyd、pyspider

(3) 引入日志

描述下 scrapy 框架运行的机制?

从 start_urls 里获取第一批 url 并发送请求,请求由引擎交给调度器入请求队列,获取完毕后,调度器将请求队列里的请求交给下载器去获取请求对应的响应资源,并将响应交给自己编写的解析方法做提取处理:

(1) 如果提取出需要的数据,则交给管道文件处理;

(2) 如果提取出 url,则继续执行之前的步骤(发送 url 请求,并由引擎将请求交给调度器入队列…),直到请求队列里没有请求,程序结束。

谈谈你对 Scrapy 的理解?

scrapy 是一个为了爬取网站数据,提取结构性数据而编写的应用框架,我们只需要实现少量代码,就能够快速的抓取到数据内容。Scrapy 使用了 Twisted 异步网络框架来处理网络通讯,可以加快我们的下载速度,不用自己去实现异步框架,并且包含了各种中间件接口,可以灵活的完成各种需求。

scrapy 框架的工作流程:

(1) 首先 Spiders(爬虫)将需要发送请求的 url(requests)经 ScrapyEngine(引擎)交给 Scheduler(调度器)

(2) Scheduler(排序,入队)处理后,经 ScrapyEngine,DownloaderMiddlewares(可选,主要有 User_Agent, Proxy 代理)交给 Downloader

(3) Downloader 向互联网发送请求,并接收下载响应(response)。将响应(response)经ScrapyEngine,SpiderMiddlewares(可选)交给 Spiders

(4)Spiders 处理 response,提取数据并将数据经 ScrapyEngine 交给 ItemPipeline 保存(可以是本地,可以是数据库)。提取 url 重新经 ScrapyEngine 交给 Scheduler 进行下一个循环。直到无 Url 请求程序停止结束

什么是增量爬取?

增量爬取即保存上一次状态,本次抓取时与上次比对,如果不在上次的状态中,便视为增量,保存下来。对于 scrapy 来说,上一次的状态是抓取的特征数据和上次爬取的 request 队列(url 列表),request 队列可以通过 request 队列可以通过 scrapy.core.scheduler 的 pending_requests 成员得到,在爬虫启动时导入上次爬取的特征数据,并且用上次 request 队列的数据作为 start url 进行爬取,不在上一次状态中的数据便保存。

选用 BloomFilter 原因:对爬虫爬取数据的保存有多种形式,可以是数据库,可以是磁盘文件等,不管是数据库,还是磁盘文件,进行扫描和存储都有很大的时间和空间上的开销,为了从时间和空间上提升性能,故选用 BloomFilter 作为上一次爬取数据的保存。保存的特征数据可以是数据的某几项,即监控这几项数据,一旦这几项数据有变化,便视为增量持久化下来,根据增量的规则可以对保存的状态数据进行约束。比如:可以选网页更新的时间,索引次数或是网页的实际内容,cookie 的更新等。

爬虫向数据库存数据开始和结束都会发一条消息,是 scrapy 哪个模块实现的?

Scrapy 使用信号来通知事情发生,因此答案是 signals 模块。

爬取下来的数据如何去重,说一下具体的算法依据?

(1)通过 MD5 生成电子指纹来判断页面是否改变

(2) nutch 去重。nutch 中 digest 是对采集的每一个网页内容的 32 位哈希值,如果两个网页内容完全一样,它们的 digest 值肯定会一样。

数据量不大时,可以直接放在内存里面进行去重,python 可以使用 set()进行去重。当去重数据需要持久化时可以使用 redis 的 set 数据结构。

当数据量再大一点时,可以用不同的加密算法先将长字符串压缩成 16/32/40 个字符,再使用上面两种方法去重。

当数据量达到亿(甚至十亿、百亿)数量级时,内存有限,必须用“位”来去重,才能够满足需求。Bloomfilter 就是将去重对象映射到几个内存“位”,通过几个位的 0/1 值来判断一个对象是否已经存在。

然而 Bloomfilter 运行在一台机器的内存上,不方便持久化(机器 down 掉就什么都没啦),也不方便分布式爬虫的统一去重。如果可以在 Redis 上申请内存进行 Bloomfilter,以上两个问题就都能解决了。

simhash 最牛逼的一点就是将一个文档,最后转换成一个 64 位的字节,暂且称之为特征字,然后判断重复只需要判断他们的特征字的距离是不是<n(根据经验这个 n 一般取值为 3),就可以判断两个文档是否相似。

可见 scrapy_redis 是利用 set 数据结构来去重的,去重的对象是 request 的 fingerprint(其实就是用 hashlib.sha1()对 request 对象的某些字段信息进行压缩)。其实 fp 就是 request 对象加密压缩后的一个字符串(40 个字符,0~f)。

Scrapy 的优缺点?

优点

(1) scrapy 是异步的

(2) 采取可读性更强的 xpath 代替正则

(3)强大的统计和 log 系统

(4) 同时在不同的 url 上爬行

(5) 支持 shell 方式,方便独立调试

(6) 写 middleware,方便写一些统一的过滤器

(7) 通过管道的方式存入数据库

缺点

(1) 基于 python 的爬虫框架,扩展性比较差

(2) 基于 twisted 框架,运行中的 exception 是不会干掉 reactor(反应器),并且异步框架出错后是不会停掉其他任务的,数据出错后难以察觉。

什么是 scrapy-redis 中的指纹?是如何去重的?

指纹

通过 sha1 加密,把请求体,请求方式,请求 url 放在一起。然后进行 16 进制的转义符字符串生成指纹。生成一个字符串,放到数据库中作为唯一标示。

去重

url 中按照 url 去重:

(1) 按照 url 去重,有一个列表,发送请求之前从数据表中看一下这个 url有没有请求过,请求过了就不用看了

(2) 内容判断,从数据库中查数据的表示,如果请求过了就在不在请求了。

怎么设置深度爬取?

通过在 settings.py 中设置 depth_limit 的值可以限制爬取深度,这个深度是与 start_urls 中定义 url 的相对值。也就是相对 url 的深度。若定义 url 为 http://www.domz.com/game/,depth_limit=1 那么限制爬取的只能是此 url 下一级的网页。深度大于设置值的将被忽视。

scrapy 和 scrapy-redis 有什么区别?为什么选择 redis 数据库?

scrapy 是一个 Python 爬虫框架,爬取效率极高,具有高度定制性,但是不支持分布式。而scrapy-redis 一套基于 redis 数据库、运行在 scrapy 框架之上的组件,可以让 scrapy 支持分布策略,Slaver 端共享 Master 端 redis 数据库里的 item 队列、请求队列和请求指纹集合。

为什么选择 redis 数据库,因为 redis 支持主从同步,而且数据都是缓存在内存中的,所以基于 redis的分布式爬虫,对请求和数据的高频读取效率非常高。

分布式爬虫主要解决什么问题?

主要解决一下 4 个问题:

(1) ip

(2) 带宽

(3) cpu

(4) io

什么是反向代理?作用是什么?

代理可以假扮 Web 服务器。这些被称为替换物(surrogate)或反向代理(reverse proxy)的代理接收发送给 Web 服务器的真实请求,但与 Web 服务器不同的是,它们可以发起与其他服务器的通信,以便按需定位所请求的内容。

可以用这些反向代理来提高访问慢速 Web 服务器上公共内容的性能。在这种配置中,通常将这些反向代理称为服务器加速器(server accelerator)。还可以将替换物与内容路由功能配合使用,以创建按需复制内容的分布式网络。

什么是分布式存储?

传统定义

分布式存储系统是大量 PC 服务器通过 Internet 互联,对外提供一个整体的服务。

分布式存储系统具有以下特性

(1) 可扩展:分布式存储系统可以扩展到几百台甚至几千台这样的一个集群规模,系统的整体性能线性增长。

(2) 低成本:分布式存储系统的自动容错、自动负载均衡的特性,允许分布式存储系统可以构建在低成本的服务器上。另外,线性的扩展能力也使得增加、减少服务器的成本低,实现分布式存储系统的自动运维。

(3) 高性能:无论是针对单台服务器,还是针对整个分布式的存储集群,都要求分布式存储系统具备高性能。

(4) 易用:分布式存储系统需要对外提供方便易用的接口,另外,也需要具备完善的监控、运维工具,并且可以方便的与其他的系统进行集成。分布式存储系统的挑战主要在于数据和状态信息的持久化,要求在自动迁移、自动容错和并发读写的过程中,保证数据的一致性。

(5) 容错:可以快速检测到服务器故障,并自动的将在故障服务器上的数据进行迁移。

(6) 负载均衡:新增的服务器在集群中保障负载均衡?数据迁移过程中保障不影响现有的服务。

(7) 事务与并发控制:实现分布式事务。

(8) 易用性:设计对外接口,使得设计的系统易于使用。

你所知道的分布式爬虫方案有哪些?

3 种分布式爬虫策略

(1) Slaver 端从 Master 端拿任务(Request/url/ID)进行数据抓取,在抓取数据的同时也生成新任务,并将任务抛给 Master。Master 端只有一个 Redis 数据库,负责对 Slaver 提交的任务进行去重、加入待爬队列。

优点: scrapy-redis 默认使用的就是这种策略,我们实现起来很简单,因为任务调度等工作 scrapy-redis 都已经帮我们做好了,我们只需要继承 RedisSpider、指定 redis_key 就行了。

缺点: scrapy-redis 调度的任务是 Request 对象,里面信息量比较大(不仅包含 url,还有 callback 函数、headers 等信息),导致的结果就是会降低爬虫速度、而且会占用 Redis 大量的存储空间。当然我们可以重写方法实现调度 url 或者用户 ID。

(2) Master 端跑一个程序去生成任务(Request/url/ID)。Master 端负责的是生产任务,并把任务去重、加入到待爬队列。Slaver 只管从 Master 端拿任务去爬。

优点: 将生成任务和抓取数据分开,分工明确,减少了 Master 和 Slaver 之间的数据交流;Master 端生成任务还有一个好处就是:可以很方便地重写判重策略(当数据量大时优化判重的性能和速度还是很重要的)。

缺点: 像 QQ 或者新浪微博这种网站,发送一个请求,返回的内容里面可能包含几十个待爬的用户 ID,即几十个新爬虫任务。但有些网站一个请求只能得到一两个新任务,并且返回的内容里也包含爬虫要抓取的目标信息,如果将生成任务和抓取任务分开反而会降低爬虫抓取效率。毕竟带宽也是爬虫的一个瓶颈问题,我们要秉着发送尽量少的请求为原则,同时也是为了减轻网站服务器的压力,要做一只有道德的 Crawler。所以,视情况而定。

3.Master 中只有一个集合,它只有查询的作用。Slaver 在遇到新任务时询问 Master 此任务是否已爬,如果未爬则加入 Slaver 自己的待爬队列中,Master 把此任务记为已爬。它和策略一比较像,但明显比策略一简单。策略一的简单是因为有 scrapy-redis 实现了 scheduler 中间件,它并不适用于非 scrapy 框架的爬虫。

优点: 实现简单,非 scrapy 框架的爬虫也适用。Master 端压力比较小,Master 与 Slaver 的数据交流也不大。

缺点:“健壮性”不够,需要另外定时保存待爬队列以实现“断点续爬”功能。各 Slaver 的待爬任务不通用。

如果把 Slaver 比作工人,把 Master 比作工头。策略一就是工人遇到新任务都上报给工头,需要干活的时候就去工头那里领任务;策略二就是工头去找新任务,工人只管从工头那里领任务干活;策略三就是工人遇到新任务时询问工头此任务是否有人做了,没有的话工人就将此任务加到自己的“行程表”。