分布式爬虫,听起来很高大上,但你如果弄懂它的模型,其实又非常简单
所谓分布式,体现在生产端和消费端的隔离,同一端的worker彼此间也是隔离的。生产端和消费端隔离,他们就可以独自进行变化,增加或是减少worker;同一端的worker彼此间也是隔离的,这样,一个worker崩溃了,其他worker不受其影响仍然可以继续工作。
消息队列
实现分布式爬取的关键是消息队列,这个问题以消费端为视角更容易理解。你的爬虫程序部署到很多台机器上,那么他们怎么知道自己要爬什么呢?总要有一个地方存储了他们需要爬取的url吧,这样他们才能工作。
假设有1万个url需要爬取,而你有100个爬虫程序部署在10台机器上,每台10个爬虫程序在运行,一个url被分给爬虫程序后,其他爬虫不能再获得这个url,不然,就重复爬取了,理解吧,分布式爬虫的关键是怎么把任务分给这些爬虫。
有些促销活动的现场会进行抽奖活动,工作人员捧着一个透明的箱子,参与活动的人从箱子的小口处把手伸进去随机拿出一张折叠的纸条,这种模式与消息队列十分相似。消息队列就好比这个抽奖的箱子,消费端的爬虫程序就好比参与活动的顾客,爬虫从消息队列里拿出一个url任务,然后进行爬取,不同于抽奖活动时一个人只能拿一次,分布式爬虫允许爬虫在结束一个爬虫任务后继续从消息队列里获取任务。
使用redis的队列做消息队列
如果你只是简单的写一个分布式爬虫,那么使用redis的队列就可以了,它完全可以作为消息队列来使用,下面的代码是生产端的代码示例
import redis
r = redis.Redis(host='127.0.0.1', port=6379,
password='yourpassword', db=1)
# 生产10个url任务
for i in range(10):
r.lpush('url_queue', i)
我这里只是做示例演示,真实的生产环境下比这要复杂的多,但核心不变,都要用lpush方法将需要爬取的任务信息写入到指定队列中,写入后,这些信息就等待爬虫程序从队列里获取,下面的代码是消费端爬虫程序的示例
import time
import redis
r = redis.Redis(host='127.0.0.1', port=6379,
password='yourpassword', db=1)
# 爬虫程序
def crawl(task):
print('爬取url{task}'.format(task=task))
time.sleep(1)
while True:
task = r.rpop('url_queue')
if not task:
time.sleep(1)
continue
crawl(task)
爬虫程序要不停的使用rpop方法从redis的队列中拉取数据进行爬取,考虑到生产者随时都可能写入新的任务,因此,必须使用死循环,得不到消息时,sleep一段时间,继续获取消息。
关于部署
生产端的程序,消费端的程序可以随意部署,但不管如何,都要确保这些机器与redis所在的机器是网络连通的,因为他们一个要写消息到redis,一个要从redis拉取任务,至于生产端和消费端是否网络连通则没有任何必要,甚至同一端的worker之间也不必网络连通
数据存储
消费端的worker在爬取数据以后,可以根据实际需要决定是否直接落库,如果需要对数据进行规范化处理或是其他操作,则可以写入新的消息队列中,由下一个工序的worker拉取数据进行处理