美文网首页Python
python爬虫010-IP代理池的维护——(1)存储模块

python爬虫010-IP代理池的维护——(1)存储模块

作者: DKider | 来源:发表于2019-04-05 20:15 被阅读18次

清明时节雪纷纷,路上行人欲断魂。借问寝室和处在,室友遥指积雪痕。

为什么要用IP代理,我就不多说了。直接进入正题。

我们在使用爬虫时需要换代理时,总是希望能找到稳定快速的代理,但是网上大多数免费代理的很多都是不可用的,每次换都要先判断这个代理是否可用。为了省去这一麻烦的步骤,IP代理池就出现了。

我的目的是自己维护一个的代理池,他定时获取新的代理,定时的去检测代理的可用性,并赋予权值、分数,提供api接口,我们需要的时候直接请求api就行了。

分为四大模块:

  • 存储模块
  • 获取模块
  • 检测模块
  • 接口模块

其中存储模块是中枢,连接着其他三个模块。

获取模块----------->存储模块<--------------->检测模块
                      |
                      |
                      |
                      |
                      V
                   接口模块

大概是这么个结构。

今天先说存储模块 :
这里我用的Redis数据库的有序集合来存储代理,因为集合具有唯一性等特点,可以去重,而且集合有序,可以方便查找。

直接上代码:中间遇到了不少坑,下面说

import redis
from random import choice

MAX_SCORE = 100
MIN_SCORE = 0
INITIAL_SCORE = 10
REDIS_HOST = 'localhost'
REDIS_PORT = 6379
REDIS_PASSWORD = None
REDIS_KEY = 'proxies'


class PoolEmptyError(Exception):
    def __init__(self, error_info='IP代理池为空,无法提供有效代理'):
        # super().__init__(self)
        self.error_info = error_info

    def __str__(self):
        return self.error_info


class RedisClient:
    """定义一个Redis服务器

    连接本地Redis数据库,并提供相关方法"""
    def __init__(self, host=REDIS_HOST, port=REDIS_PORT,
                 password=REDIS_PASSWORD):
        """连接接数据库

        :param host: Redis地址
        :param port: Redis地址
        :param password: Redis密码"""
        self.db = redis.StrictRedis(host=host, port=port, password=password,
                                    decode_responses=True)

    def add(self, proxy, score=INITIAL_SCORE):
        """添加新的IP代理

        :param proxy: 代理
        :param score: 分数
        :return : 添加结果"""
        if not self.db.zscore(REDIS_KEY, proxy):
            return self.db.zadd(REDIS_KEY, {proxy: score})

    def random(self):
        """随机返回一个代理

        如果有100分的代理,随机返回一个;
        如果没有100分的,则按照分数排名获取分数最高的
        如果都没有则返回异常

        :return: 随机代理"""
        result = self.db.zrangebyscore(REDIS_KEY, MAX_SCORE, MAX_SCORE)
        if len(result):
            return choice(result)
        else:
            result = self.db.zrevrange(REDIS_KEY, 0, 100)
            if len(result):
                return choice(result)
            else:
                raise PoolEmptyError

    def decrease(self, proxy):
        """将检测出不可用的代理的分数减一分,如果分数小于最小值,则从代理池中删除

        :param proxy: 代理地址及端口
        :return: 修改后的代理分数"""
        score = self.db.zscore(REDIS_KEY, proxy)
        if score and score > MIN_SCORE:
            print('代理', proxy, '当前分数', score, '减1')
            return self.db.zincrby(REDIS_KEY, -1, proxy)
        else:
            print('代理', proxy, '当前分数', score, '删除')
            return self.db.zrem(REDIS_KEY, proxy)

    def exists(self, proxy):
        """判断IP代理是否存在

        :param proxy: 代理ip
        :return: 是否存在->bool"""
        return self.db.zscore(REDIS_KEY, proxy) is not None

    def max(self, proxy):
        """将代理的分数设置为MAX_SCORE

        :param proxy: 代理ip
        :return: 设置结果"""
        print('代理', proxy, '可用,设置为',MAX_SCORE)
        return self.db.zadd(REDIS_KEY, {proxy: MAX_SCORE})

    def count(self):
        """返回数据库中的代理数量

        :return: 数量->int"""
        return self.db.zcard(REDIS_KEY)

    def all(self):
        """返回数据库中的所有代理

        :return: 全部代理"""
        return self.db.zrangebyscore(REDIS_KEY, MIN_SCORE, MAX_SCORE)


# p=RedisClient()
# proxy = '11.1.1.1:8080'
# p.add(proxy, 10)
# p.decrease(proxy)
# print(p.exists(proxy))
# p.max(proxy)
# print(p.count())
# print(p.all())
"""
获取模块----------->存储模块<--------------->检测模块
                      |
                      |
                      |
                      |
                      V
                   接口模块
"""
遇到的坑:
  • 对自定义异常类不熟悉,定义了一个PoolEmptyError,而且也不清楚什么时候应该报异常,而不是过滤错误。
  • redis库在操作数据库时有序结合的一个方法:zadd(),这个函数,书上和网上给的例子都是:zadd(redis_key, 'bob', 10)或者zadd(redis_key, 10, 'bob'),等类似这样的调用方法,但是这样会报错。自己试一下就知道了,我忘记怎么说的了。正确的用法:zadd(redis_key, {'bob': 100})
  • 判断是否为空时要用:is | is not ,而不是==
  • 筛选函数zincrby() 的调用,网上书上是:zincrby(redis_key, 'bob',-1) 类似这样的调用,本意是让bob的score减一,但是他参数写反了,应该是:zincrby(redis_key, -1,'bob')

最后,我用下面的注释写了简单的单元测试,保证每一个方法都可以正常调用。

明天写获取模块。

相关文章

  • python爬虫010-IP代理池的维护——(1)存储模块

    清明时节雪纷纷,路上行人欲断魂。借问寝室和处在,室友遥指积雪痕。 为什么要用IP代理,我就不多说了。直接进入正题。...

  • 爬虫—代理池的维护二(存储模块)

    记录一个免费代理池的维护,主要包含四个模块:获取模块:主要负责从各个免费代理网站提取出最新发布的免费代理,获取到本...

  • asyncio实现代理池

    从一个代理池讲起? 搞爬虫的一般都有自己的代理池,代理池的结构一般分为抓取模块,存储模块,检测模块,api模块。抓...

  • 爬虫代理池的设置

    下面介绍一段python爬虫的代理池

  • 爬取IP代理偷偷给文章刷阅读量

    一、前言 原本是想开始维护IP代理池,继续python爬虫进阶之路,但在看其他人写的IP代理爬取的文章时,发现可以...

  • 爬虫之代理池维护

    前言 做过爬虫的应该都知道,在爬取反爬比较强的网站如果同一时间获取的数据量过大就会导致封IP,例如豆瓣,搜狗之类的...

  • Flask与Redis 维护代理池

    为什么要使用代理池? 许多网站反爬虫,会封IP 网上大量免费代理,利用好资源 定时的检测维护可以得到多个可用代理 ...

  • Python爬虫代理池

    爬虫代理IP池 在公司做分布式深网爬虫,搭建了一套稳定的代理池服务,为上千个爬虫提供有效的代理,保证各个爬虫拿到的...

  • Python爬虫代理池

    爬虫代理IP池 在公司做分布式深网爬虫,搭建了一套稳定的代理池服务,为上千个爬虫提供有效的代理,保证各个爬虫拿到的...

  • Python爬虫代理池

    在公司做分布式深网爬虫,搭建了一套稳定的代理池服务,为上千个爬虫提供有效的代理,保证各个爬虫拿到的都是对应网站有效...

网友评论

    本文标题:python爬虫010-IP代理池的维护——(1)存储模块

    本文链接:https://www.haomeiwen.com/subject/hrukiqtx.html