scrapy-redis源码解析

目录

Connection————建立redis连接

defaults: 默认参数配置

dupefilter: 实现 request 去重

Picklecompat:

pipelines: 默认数据收集存储

queue: 任务调度队列

spiders: 赋予 Scrapy spiders 远程调度

总结


Connection————建立redis连接

SETTINGS_PARAMS_MAP: 用于将 Redis 参数名映射到 redis 库的参数名。

get_redis_from_settings 函数: 从 Scrapy 的 settings 对象获取连接 参数并调用

get_redis 建立 Redis 连接。

get_redis 函数: 辅助函数,从传入参数建立 Redis 连接。

代码:

import six

from scrapy.utils.misc import load_object

from . import defaults


# Shortcut maps 'setting name' -> 'parmater name'.
#这是一个参数映射,左边设置名称,右边是参数名称
SETTINGS_PARAMS_MAP = {
    'REDIS_URL': 'url',
    'REDIS_HOST': 'host',
    'REDIS_PORT': 'port',
    'REDIS_ENCODING': 'encoding',
}

#获取链接redis参数
def get_redis_from_settings(settings):
    """Returns a redis client instance from given Scrapy settings object.

    This function uses ``get_client`` to instantiate the client and uses
    ``defaults.REDIS_PARAMS`` global as defaults values for the parameters. You
    can override them using the ``REDIS_PARAMS`` setting.

    Parameters
    ----------
    settings : Settings
        A scrapy settings object. See the supported settings below.

    Returns
    -------
    server
        Redis client instance.

    Other Parameters
    ----------------
    REDIS_URL : str, optional
        Server connection URL.
    REDIS_HOST : str, optional
        Server host.
    REDIS_PORT : str, optional
        Server port.
    REDIS_ENCODING : str, optional
        Data encoding.
    REDIS_PARAMS : dict, optional
        Additional client parameters.

    """
    #将默认的redis链接参数拷贝一份给params
    params = defaults.REDIS_PARAMS.copy()
    #如果setting中没有定义REDIS_PARAMS,则使用默认的参数
    params.update(settings.getdict('REDIS_PARAMS'))
    # XXX: Deprecate REDIS_* settings.
    #将setting中的REDIS_URL,REDIS_HOST,REDIS_PORT,REDIS_ENCODING,更新进入params中,没有则使用默认的
    for source, dest in SETTINGS_PARAMS_MAP.items():
        val = settings.get(source)
        if val:
            params[dest] = val

    # Allow ``redis_cls`` to be a path to a class.
    if isinstance(params.get('redis_cls'), six.string_types):
        params['redis_cls'] = load_object(params['redis_cls'])
        #get_redis(**params) = redis_cls(from_setting) = redis链接
        #from_setting() = get_redis_from_setting() = get_redis(**params) = redis_cls(**kwargs) = redis链接


    return get_redis(**params)


# Backwards compatible alias.
from_settings = get_redis_from_settings

#获取redis的链接
def get_redis(**kwargs):
    """Returns a redis client instance.

    Parameters
    ----------
    redis_cls : class, optional
        Defaults to ``redis.StrictRedis``.
    url : str, optional
        If given, ``redis_cls.from_url`` is used to instantiate the class.
    **kwargs
        Extra parameters to be passed to the ``redis_cls`` class.

    Returns
    -------
    server
        Redis client instance.

    """
    #从参数字典中获取redis_cls,没有则使用默认的
    #redis_cls = defaults.RENIS_CLS = Redis
    redis_cls = kwargs.pop('redis_cls', defaults.REDIS_CLS)
    url = kwargs.pop('url', None)
    if url:
        #使用url连接redis,优先使用
        return redis_cls.from_url(url, **kwargs)
    else:
        #使用参数连接redis,Redis(**kwargs) 返回redis连接
        return redis_cls(**kwargs)

 

 

 

defaults: 默认参数配置

Redis 连接参数

REDIS_CLS

REDIS_ENCODING

REDIS_PARAMS

 

PIPELINE_KEY: 数据存储在 Redis 的 key

SCHEDULER_*: 参与任务调度的 Redis key,包括 requests 存储,调度队 列类型选择, 去重指纹存储

START_URLS_*: 任务注入 Redis 的 key 和 数据类型

代码:

 

import redis


# For standalone use.
DUPEFILTER_KEY = 'dupefilter:%(timestamp)s'

PIPELINE_KEY = '%(spider)s:items'

REDIS_CLS = redis.StrictRedis
REDIS_ENCODING = 'utf-8'
# Sane connection defaults.
REDIS_PARAMS = {
    'socket_timeout': 30,
    'socket_connect_timeout': 30,
    'retry_on_timeout': True,
    'encoding': REDIS_ENCODING,
}

SCHEDULER_QUEUE_KEY = '%(spider)s:requests'
SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.PriorityQueue'
SCHEDULER_DUPEFILTER_KEY = '%(spider)s:dupefilter'
SCHEDULER_DUPEFILTER_CLASS = 'scrapy_redis.dupefilter.RFPDupeFilter'

START_URLS_KEY = '%(name)s:start_urls'
START_URLS_AS_SET = False

 

 

 

dupefilter: 实现 request 去重

RFPDupeFilter类继承来自Scrapy中的BaseDupeFilter类。

 

Scrapy去重采用的是集合实现的,Scrapy分布式中去重就要利用共享集合, 采用Redis 的集合数据结构。

 

request_seen()方法和Scrapy中的request_seen()方法相似。这里的集合 操作的是server 对象的sadd()方法操作。Scrapy中的是数据结构,这里 换成了数据库的存储方式。

 

鉴别重复的方式还是使用指纹,指纹依靠request_fingerprint()方法来获 取。获取指纹后 直接向集合中添加指纹,添加成功返回1,判定结果返 回False就是不重复。

这里完成利用Redis的集合完成指纹的记录和重复的验证。

 

### request对象什么时候入队

- dont_filter = True ,构造请求的时候,把dont_filter置为True,该 url会被反复抓取(url 地址对应的内容会更新的情况)

- 一个全新的url地址被抓到的时候,构造request请求

- url地址在start_urls中的时候,会入队,不管之前是否请求过

  - 构造start_url地址的请求时候,dont_filter = True

 

```python

def enqueue_request(self, request):

    if not request.dont_filter and self.df.request_seen(request):

        # dont_filter=False Ture  True request指纹已经存在  #不会 入队

        # dont_filter=False Ture  False  request指纹已经存在 全新 的url  #会入队

        # dont_filter=Ture False  #会入队

        self.df.log(request, self.spider)

        return False

    self.queue.push(request) #入队

    return True

```

 

### scrapy_redis去重方法

- 使用sha1加密request得到指纹

- 把指纹存在redis的集合中

- 下一次新来一个request,同样的方式生成指纹,判断指纹是否存在reids 的集合中

 

### 生成指纹

```python

fp = hashlib.sha1()

fp.update(to_bytes(request.method))  #请求方法

fp.update(to_bytes(canonicalize_url(request.url))) #url

fp.update(request.body or b'')  #请求体

return fp.hexdigest()

```

### 判断数据是否存在redis的集合中,不存在插入

```python

added = self.server.sadd(self.key, fp)

return added != 0

 

代码:

 

import logging
import time

from scrapy.dupefilters import BaseDupeFilter
from scrapy.utils.request import request_fingerprint

from . import defaults
from .connection import get_redis_from_settings


logger = logging.getLogger(__name__)


# TODO: Rename class to RedisDupeFilter.
class RFPDupeFilter(BaseDupeFilter):
    """Redis-based request duplicates filter.

    This class can also be used with default Scrapy's scheduler.

    """

    logger = logger

    def __init__(self, server, key, debug=False):
        """Initialize the duplicates filter.

        Parameters
        ----------
        server : redis.StrictRedis
            The redis server instance.
        key : str
            Redis key Where to store fingerprints.
        debug : bool, optional
            Whether to log filtered requests.

        """
        self.server = server
        self.key = key
        self.debug = debug
        self.logdupes = True


    #类方法 cls当前类本身 RFPDupeFilter
    #初始化init中的值
    @classmethod
    def from_settings(cls, settings):
        """Returns an instance from given settings.

        This uses by default the key ``dupefilter:<timestamp>``. When using the
        ``scrapy_redis.scheduler.Scheduler`` class, this method is not used as
        it needs to pass the spider name in the key.

        Parameters
        ----------
        settings : scrapy.settings.Settings

        Returns
        -------
        RFPDupeFilter
            A RFPDupeFilter instance.


  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python基于Scrapy-Redis分布式爬虫设计毕业源码(毕设项目).zip 该项目代码主要针对计算机、自动化等相关专业的学生从业者下载使用,项目代码都经过严格调试,确保可以运行!放心下载使用。 也可作为期末课程设计、课程大作业、毕业设计等。具有较高的学习借鉴价值!也可直接当做个人项目使用。 ## 开发环境:Python + Scrapy框架 + redis数据库 ## 程序开发工具: PyCharm 程序采用 python 开发的 Scrapy 框架来开发,使用 Xpath 技术对下载的网页进行提取解析,运用 Redis 数据库做分布式, 设计并实现了针对当当图书网的分布式爬虫程序,scrapy-redis是一个基于redisscrapy组件,通过它可以快速实现简单分布式爬虫程序,该组件本质上提供了三大功能: scheduler - 调度器 dupefilter - URL去重规则(被调度器使用) pipeline - 数据持久化 Scrapy是一个比较好用的Python爬虫框架,你只需要编写几个组件就可以实现网页数据的爬取。但是当我们要爬取的页面非常多的时候,单个主机的处理能力就不能满足我们的需求了(无论是处理速度还是网络请求的并发数),这时候分布式爬虫的优势就显现出来。 而Scrapy-Redis则是一个基于RedisScrapy分布式组件。它利用Redis对用于爬取的请求(Requests)进行存储和调度(Schedule),并对爬取产生的项目(items)存储以供后续处理使用。scrapy-redi重写了scrapy一些比较关键的代码,将scrapy变成一个可以在多个主机上同时运行的分布式爬虫。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值