爬虫笔记:分布式爬虫部署(Scrapy+Redis)

scrapy+redis实现分布式爬虫

前言介绍

  • 分布式爬虫又可以称为集群爬虫,和单点爬虫不同的是分布式爬虫可以实现多台机器同时运行,速度更快也能避免反爬虫机制对ip检测封锁,而且能随时停止和运行,自动url去重(这意味着再次运行必须先清空redis数据库下的XX:dupefilter,否则无法重复爬取相同url)。
  • 本文介绍redis分布式爬虫 ,redis是一种非关系数据库除了支持string类型的value外还支持string、hash、set、sortedset、list等数据结构。重要的是能支持主从复制,主机会自动将数据同步到从机,可以进行读写分离。
  • 我们利用redis的特性,更改scrapy的任务调度,我们在scrapy项目spider中建立两个爬虫一个专门爬取url网络链接,一个爬取数据。master(主机)将设置链接爬虫爬取对应的网络链接后在redis中分享url链接给slave(从机),slave领取任务后开始数据爬取并将数据存储在redis中,最后master便能通过访问redis命令提取目标数据。

1.scrapy+redis分布式准备工作:

(1).下载redis及环境搭建

redis下载地址 windows
redis desktop manager(Windows可视化管理redis数据库工具)下载地址
redis 官网最新下载地址 Linux
Linux Ubuntu系统安装介绍看这里

(2).scrapy_redis环境安装

windows安装后
  • 找到并修改安装目录下bin文件下的redis.conf
#需要远程连接redis 先注释bind
# bind 127.0.0.1

如果需要修改密码,也在redis.conf文件找到:

#取消注释requirepass
requirepass redisredis  # redisredis就是密码(记得自己修改)
  • 运行redis服务器的命令:安装目录下的redis-server.exe
  • 运行redis客户端的命令:安装目录下的redis-cli.exe,也可以在当前目录下打开dos输入redis-cli 然后auth 密码
Linux安装后
  • 进入目录下 同样修改redis.conf文件
  • 输入/etc/redis-serverce redis.conf启动redis服务器
  • 当前目录 重新打开一个新窗口 输入/etc/redis-cli (一般在etc目录下)
然后在Linux或Windows的命令窗口输入命令安装对应的python库
 pip install scrapy-redis 
 pip install redis

我们可以在windows下测试远程连接redis是否正常
打开dos窗口输入
redis-cli -h 远程ip地址 -p 6379
注意要提前打开远程机器的redis-server,当然你也可以通过redis desktop manager图形化界面工具来连接管理你的redis数据库。

(3)介绍redis有关操作命令

网上可以搜到很多有关redis的操作命令,我这里介绍比较常见的

D:\Program Files\Redis>redis-cli      #启动redis客户端
redis 127.0.0.1:6379> auth 123456   #输入密码
OK
redis 127.0.0.1:6379> keys *  #查询当前所有数据表
(empty list or set)
redis 127.0.0.1:6379> select 1 #选择数据库db=1 默认是0
OK
redis 127.0.0.1:6379[1]> keys *
1) "ifengspider:items"
3) "ifengurls:dupefilter"
4) "newspider:dupefilter"
5) "ifeng:requests"
redis 127.0.0.1:6379[1]> lrange ifengspider:items 1 2       #读取ifengspider:item表前2行数据
#这里数据过多将无法显示,在windows下使用redis desktop manager可查看更多,但也很容易崩溃,不是很稳定
redis 127.0.0.1:6379[1]> flushdb #只清空当前数据库数据
redis 127.0.0.1:6379[1]> flushall #清空所有数据库数据

2、scrapy中使用加入redis

(1)在setting.py文件的添加:

SCHEDULER = "scrapy_redis.scheduler.Scheduler"
SCHEDULER_PERSIST = True
SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.SpiderPriorityQueue'
REDIS_URL =  'redis://:password@hostname:6973' # 此处设置将连接有密码的host
REDIS_HOST = '127.0.0.1' #如果设置了REDIS_URL此处将被覆盖
REDIS_PORT = 6379 #同上

#启用Redis调度存储请求队列
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
 
#确保所有的爬虫通过Redis去重
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
--------------------------------以下选填-----------------------------------------------
#默认请求序列化使用的是pickle 但是我们可以更改为其他类似的。PS:这玩意儿2.X的可以用。3.X的不能用
#SCHEDULER_SERIALIZER = "scrapy_redis.picklecompat"
 
#不清除Redis队列、这样可以暂停/恢复 爬取
#SCHEDULER_PERSIST = True
 
#使用优先级调度请求队列 (默认使用)
#SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.PriorityQueue'
#可选用的其它队列
#SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.FifoQueue'
#SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.LifoQueue'
 
#最大空闲时间防止分布式爬虫因为等待而关闭
#SCHEDULER_IDLE_BEFORE_CLOSE = 10
 
#此处设置将返回的数据自动保存在连接主机的redis数据库中
ITEM_PIPELINES = {
    'scrapy_redis.pipelines.RedisPipeline': 300
}
 
#序列化项目管道作为redis Key存储
#REDIS_ITEMS_KEY = '%(spider)s:items'
 
#默认使用ScrapyJSONEncoder进行项目序列化
#You can use any importable path to a callable object.
#REDIS_ITEMS_SERIALIZER = 'json.dumps'
 
#指定连接到redis时使用的端口和地址(可选)
#REDIS_HOST = 'localhost'
#REDIS_PORT = 6379
 
#指定用于连接redis的URL(可选)
#如果设置此项,则此项优先级高于设置的REDIS_HOST 和 REDIS_PORT
#REDIS_URL = 'redis://user:pass@hostname:9001'
 
#自定义的redis参数(连接超时之类的)
#REDIS_PARAMS  = {}
 
#自定义redis客户端类
#REDIS_PARAMS['redis_cls'] = 'myproject.RedisClient'
 
#如果为True,则使用redis的'spop'进行操作。
#如果需要避免起始网址列表出现重复,这个选项非常有用。开启此选项urls必须通过sadd添加,否则会出现类型错误。
#REDIS_START_URLS_AS_SET = False
 
#RedisSpider和RedisCrawlSpider默认 start_usls 键
#REDIS_START_URLS_KEY = '%(name)s:start_urls'
 
#设置redis使用utf-8之外的编码
#REDIS_ENCODING = 'latin1'

(2)在其他文件中,若要通过代码来连接redis数据库以此操作redis数据库中数据:

pool =redis.ConnectionPool(host='hostname', port=6379, db=0,password=‘pwd’)
#password没有设置可不填
r = redis.Redis(connection_pool=pool)
r.lpush('XX:items’,data) #将数据data导入连接的数据库中 字段名XX:items
data = r.lpop('XX:items') #从数据库中XX:items字段最上方数据弹出
r.delete('aa:items','bb:items')  #清空 aa:items,bb:items中数据 如果需要反复跑程序可在爬虫开始时设置,来删除对应的dupefilter

(3)关于item.py和middlewares.py:

这里和scrapy下写法没什么区别,都照原来写就好了

(4)关于自定义spider写法:

主要不同的是爬虫类需要继承scrapy_redis中的RedisCrawlSpider而不是scrapy.Spider,然后start_urls改成redis_key来从redis服务端提取url任务链接地址。这样主机有专门爬取链接的爬虫,其它从机只需要领取其中的任务,解析任务页面返回数据,即可实现分布式爬虫。

import scrapy
from scrapy_redis.spiders import RedisCrawlSpider  
class XX_spider(RedisCrawlSpider):  #继承scrapy_redis
    name = 'ifengspider'
    allowed_domains=['finance.ifeng.com','app.finance.ifeng.com']
    redis_key = 'XX:requests'  #填任务url链接所在表名,在setting.py中已经设置好要连接的redis-server和对应db
    def parse(self,response):  #这里直接写scrapy默认的parse,到这里就可以直接写对应网页解析了

(5)pipelines.py 将Spider文件返回的数据传入redis数据库中:

首先在setting.py文件下设置
ITEM_PIPELINES = {
'scrapy_redis.pipelines.RedisPipeline': 300 
}
pipelines.py :
class Redis_pipelines(object):
  def process_item(self,item,spider):
    item['a']=item['a']  #这里作为处理结果的收尾地方,可以按自己意愿改写数据
    item['b']=item['b']
    item['c']=item['c']
    item['d']=item['d']
    return item

这里会使每台机器跑下来的数据会发送到setting.py设置的主机地址redis服务器上,字段名为XXspider:items。最后所有url跑完后,在服务器写一个mysql.py文件:连接本地redis服务器导出所有数据给自己的Mysql数据库。下面我贴上Pipelines.py的改写代码和提取redis数据循环导入mysql数据库中代码。

Mysql.py:
import pymysql
import redis
import json
def Data_into_Mysql():

        conn = pymysql.connect(host='127.0.0.1',
                                 charset='utf8',user='root',passwd='pwd',db='crawler',port=3306)
        pool =redis.ConnectionPool(host='127.0.0.1', port=6379, password='pwd',db=2)
        r = redis.Redis(connection_pool=pool)
        data = r.lpop('xx:items')
        count=0
        while data is not None:
            try:
                data=data.decode('utf-8','ignore')
                a=json.loads(data)           
                with conn.cursor() as cursor:                         
                        sql = '''INSERT INTO table  VALUES(%s,%s)'''
                        cursor.execute(sql, (a['key'],a['key']))                 
                        conn.commit() 
                count+=1
                print(count)
                data = r.lpop('xx:items')
            except Exception as e:
                print(str(e))
                print(a)
                data = r.lpop('xx:items')
        conn.close();
Data_into_Mysql()

注意如果要重新跑相同的url,请先清空对应dupefilter里的数据,此处为redis去重设置。

结语

作为爬虫新手,第一次写自己爬虫学习上的一些经历,若有不足之处欢迎指出,欢迎感兴趣的朋友一起探讨研究,分布式爬虫学习也是在网上到处翻了各种资料对比填坑才成功实现,我希望自己总结的经验能方便更多感兴趣的小伙伴,若写有不明之处,可以留言或私信我,当然特别细节的东西可能我也需要继续学习。后续可能会继续更新分布式爬虫相关的内容和填坑,谢谢大家支持。

参考资料:

基于Python使用scrapy-redis框架实现分布式爬虫
使用scrapy-redis构建简单的分布式爬虫

如果您喜欢我的文章,请关注或点击喜欢,您的支持是我最大的动力 ^ ^~!
转载请注明作者黑羊的皇冠及其出处

黑羊的皇冠 简书主页
github地址也将尽快放出来

    原文作者:黑羊的皇冠
    原文地址: https://www.jianshu.com/p/966fd26b2f8d
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞