爬虫工作量由小到大的思维转变---<第四十章 Scrapy Redis 实现IP代理池管理的最佳实践>

前言:

本篇是要结合上篇一起看的姊妹篇:爬虫工作量由小到大的思维转变---<第三十九章 Scrapy-redis 常用的那个RetryMiddleware>-CSDN博客

IP代理池的管理对于确保爬虫的稳定性和数据抓取的匿名性至关重要。围绕Scrapy-Redis框架和一个具体的IP代理池中间件代码,在分布式爬虫中如何使用Redis实现IP代理池的管理,这篇文章进行探讨一下  (当然,还有更好的方案,希望大家反驳我)

正文:

IP代理池与Scrapy-Redis的结合

源代码

import random
import time
import redis

class RedisProxyMiddleware(object):
    def __init__(self, redis_host, redis_port, redis_db, proxy_key, batch_size, max_failures, lock_key):
        # 初始化Redis连接
        self.redis = redis.StrictRedis(host=redis_host, port=redis_port, db=redis_db)
        # Redis 代理池key
        self.proxy_key = proxy_key
        # 每次从Redis获取代理的数量
        self.batch_size = batch_size
        # 代理IP允许的最大失败次数
        self.max_failures = max_failures
        # 代理IP锁的key
        self.lock_key = lock_key
        # 本地缓存代理IP的集合
        self.cached_proxies = set()

    @classmethod
    def from_crawler(cls, crawler):
        settings = crawler.settings
        # 创建中间件实例并返回
        return cls(
            redis_host=settings.get('REDIS_HOST'),
            redis_port=settings.get('REDIS_PORT'),
            redis_db=settings.get('REDIS_DB'),
            proxy_key=settings.get('REDIS_PROXY_KEY'),
            batch_size=settings.get('BATCH_SIZE'),
            max_failures=settings.get('MAX_FAILURES'),
            lock_key=settings.get('REDIS_PROXY_LOCK_KEY')
        )

    def process_request(self, request, spider):
        # 如果请求中没有代理IP,或者请求中的代理IP已经被加入到了代理锁
        if 'proxy' not in request.meta or self.redis.sismember(self.lock_key, request.meta['proxy']):
            # 如果缓存的代理IP数量小于批量大小,则尝试获取新的代理IP
            if len(self.cached_proxies) < self.batch_size:
                self.fetch_proxies(spider)
            # 如果本地缓存中有代理IP,从中随机选择一个
            if self.cached_proxies:
                request.meta['proxy'] = random.choice(list(self.cached_proxies))

    def fetch_proxies(self, spider):
        # 尝试获取代理锁,如果获取锁成功,则进行代理IP的刷新
        if self.acquire_lock(spider):
            try:
                spider.logger.debug('代理锁已获取,准备提取新的代理IP。')
                fetched_proxies = self.redis.srandmember(self.proxy_key, self.batch_size)
                if fetched_proxies:
                    # 清空本地代理IP缓存,并添加新获取的代理IP
                    self.cached_proxies.clear()
                    self.cached_proxies.update(fetched_proxies)
                    spider.logger.debug('已提取{}个新的代理IP。'.format(len(fetched_proxies)))
                else:
                    spider.logger.warning('无法获取到新的代理IP,继续使用现有的代理IP。')
            finally:
                # 无论提取代理IP成功与否,都释放代理锁
                self.release_lock(spider)
                spider.logger.debug('代理锁已释放。')
        else:
            # 如果没有获取到代理锁,则等待,等待时间应根据实际情况调整
            spider.logger.debug('代理锁正被其他实例占用,等待重试。')
            time.sleep(5)

    def acquire_lock(self, spider):
        # 尝试加锁,用于控制代理IP的获取
        status = self.redis.set(self.lock_key, 1, nx=True, ex=60)  # 锁的有效期设为60秒
        if status:
            spider.logger.debug('代理锁已加锁。')
        else:
            spider.logger.debug('代理锁加锁失败,锁已存在。')
        return status

    def release_lock(self, spider):
        # 释放锁,其他实例可以继绀获取新代理
        self.redis.delete(self.lock_key)
        spider.logger.debug('代理锁已释放。')
RedisProxyMiddleware代码解析:

可以细分为几个重要部分,每个部分都有特定的目的。我们将对这些部分进行详细解析:

  1. 初始化和属性赋值 __init__方法中实现了RedisProxyMiddleware的初始化方法。它接收来自Scrapy的参数,如Redis数据库的连接信息、代理关键字、批量大小、最大失败次数和锁定键。这些参数在实例化时保存为类的属性,以供后续使用。此外,还初始化了一个空集合用于缓存代理IP。

  2. from_crawler方法 from_crawler方法是一个类方法,用于从Crawler对象获取参数,并实例化RedisProxyMiddleware类。通过获取Scrapy设置中的Redis连接信息和其他参数,我们可以方便地初始化中间件并与Redis建立连接。

  3. process_request方法 process_request方法是RedisProxyMiddleware中的关键方法,用于处理Spider请求以获取代理IP。在该方法中,首先检查请求中是否存在代理IP(存储在请求的meta数据中),以及该代理IP是否在锁定键指定的Redis集合中。如果请求中没有代理IP或代理IP被锁定,将调用fetch_proxies方法来获取新的代理IP。

  4. fetch_proxies方法 fetch_proxies方法用于从Redis数据库获取一组全新的代理。通过使用srandmember方法,它从Redis中的代理关键字指定的集合中获取指定数量的随机代理IP。如果成功获取到代理IP,则将其添加到cached_proxies集合中。然后,根据获取的代理IP数量,记录调试日志或警告信息,以供进一步的调试和分析。

总结:

RedisProxyMiddleware在Scrapy框架中实现了一个IP代理池的管理中间件。通过对代码进行解析,我们了解了它的初始化方法、参数设置、处理请求方法和获取全新代理IP的逻辑。RedisProxyMiddleware的设计目标是提供一个简单、可扩展和稳定的IP代理池解决方案,以满足分布式爬虫的需求。通过精确管理代理IP,并根据需要进行动态调整和切换,我们可以提高爬虫的稳定性和数据抓取效率。

你可能感兴趣的:(scrapy爬虫开发,爬虫,scrapy)