美文网首页Python系列程序员
‘人生苦短、我用Python’ 之 Redis+Flask维护免

‘人生苦短、我用Python’ 之 Redis+Flask维护免

作者: 书写不简单 | 来源:发表于2018-09-14 19:52 被阅读0次

    如今,‘人生苦短、我用Python’ 再非一句戏言........


    Python.png

      我们在爬取网上数据的时候经常会遇到IP被封的情况,解决这种问题我们无非就是替换IP,一般替换IP有两种方法:网上获取免费代理、购买代理。但是不管哪种方式获得代理,都不能保证100%的有效,因为一个IP可能被很多人使用,如果别人也使用了同样的代理访问网站,然后被封,那这个代理的使用价值就大打折扣了!或者代理服务器突然发生故障或者网络不给力,这些情况都会降低我们的开发效率。

      所以,我们有必要提前对代理做个筛选,保留可用的代理,剔除被封掉的代理。在我们使用的时候直接从众多可用代理中选择一个即可。

      本文将详细讲解Python的实战、本文中的demo使用到了RedisFlask、asyncio、aiohttp、metaclass、requests、pyquery、redis-py等,想一步到位got源码的同学点击 demo 即可。

    先简要介绍一下代理池的架构:

    代理池架构.jpg

    代理池一共分为四个模块:获取模块、存储模块、检测模块、接口模块

    • 获取模块
      该模块定时从代理网站获取代理,然后保存到数据库中备用
    • 存储模块
      该模块可以说是个中心模块,把其他模块串联起来,使用的是Redis集合
    • 检测模块
      定时从存储模块中取出代理检测,根据结果对代理进行重新标识
    • 接口模块
      接口模块通过Web API提供服务接口, 接口通过连接数据库并通过Web形式返回可用的代理

    我们分别介绍一下几个模块的实现

    • 获取模块

    该模块的实现相对简单,先上码(上马)

    from pyquery import PyQuery as pq
    import re
    
    class ProxyMetaclass(type):
        def __new__(cls, name, bases, attrs):
            count = 0
            attrs['__CrawlFunc__'] = []
            for k, v in attrs.items():
                if 'crawl_' in k:
                    attrs['__CrawlFunc__'].append(k)
                    count += 1
            attrs['__CrawlFuncCount__'] = count
            return type.__new__(cls, name, bases, attrs)
    
     # eval()函数的使用
     # 字符串拼接
    class Crawler(object, metaclass=ProxyMetaclass):
        def get_proxies(self, callback):
            """
            获取抓取的代理
            :param callback: 抓取代理的方法
            :return:
            """
            proxies = []
            for proxy in eval("self.{}()".format(callback)):
                print("成功获取代理:  " + proxy)
                proxies.append(proxy)
            return proxies
    
         # 66代理
        # join() 方法用于将序列中的元素以指定的字符连接生成一个新的字符串
        def crawl_proxy66(self, page_count=6):
            """
            获取66代理
            :param page_count: 指定页码
            :return:
            """
            start_url = 'http://www.66ip.cn/{}.html'
            urls = [start_url.format(page) for page in range(1, page_count + 1)]
            for url in urls:
                html = get_page(url)
                if html:
                    doc = pq(html)
                    trs = doc('.containerbox table tr:gt(0)').items()
                    for tr in trs:
                        ip = tr.find('td:nth-child(1)').text()
                        port = tr.find('td:nth-child(2)').text()
                        yield ':'.join([ip, port])
    

     该模块中用到了元类metaclas、pyquery。对python中的metaclass不熟悉的同学可以点进去看看。这里为了缩短篇幅,代码中只包含一个网站的抓取,其它网站的代理抓取可以通过demo查看。
     该类中定义了一个ProxyMetaclass元类,用来聚集抓取方法。重写了new方法,里面的四个参数是固定的,其中attrs这个属性包含了类的所有属性。通过便利该属性,获取到当前类中的所有抓取方法,然后将方法加入到CrawlFunc这个属性中,这样就可以在其他地方获取到方法并调用。如果想扩展的话可以自定义方法,并以crawl开头即可。
     此类中还定义了一个调用方法的方法,即:get_proxies,该方法的参数是方法名字,使用eval()函数,将字符串转变成方法调用,获取代理之后,代理列表作为参数返回即可。

      既然定义了Crawler类用来抓取代理,接下来就再定义一个Getter类,用来动态调用所有以crawl开头的方法,然后获取抓取到的代理,保存到数据库中。

    from proxypool.tester import Tester
    from proxypool.db import RedisClient
    from proxypool.crawler import Crawler
    from proxypool.setting import *
    import sys
    
    class Getter():
        def __init__(self):
            self.redis = RedisClient()
            self.crawler = Crawler()
        
        def is_over_threshold(self):
            """
            判断是否达到了代理池限制
            """
            if self.redis.count() >= POOL_UPPER_THRESHOLD:
                return True
            else:
                return False
        
        def run(self):
            print('获取器开始执行')
            if not self.is_over_threshold():
                for callback_label in range(self.crawler.__CrawlFuncCount__):
                    callback = self.crawler.__CrawlFunc__[callback_label]
                    # 获取代理
                    proxies = self.crawler.get_proxies(callback)
                    sys.stdout.flush()
                    for proxy in proxies:
                        self.redis.add(proxy)
    

     Getter类就是一个获取类,它定义了代理池能容纳的最大数量POOL_UPPER_THRESHOLD,也定义了is_over_threshold()方法判断是否达到了最大容量。该方法里面调用了RedisClient的count方法获取数据库中的代理数量进行判断。
     接着定义了run()方法,该方法首先判断了代理池是否达到了最大值,然后再调用获取方法,得到的代理加入数据库,这样获取模块的工作就完成了。

    • 存储模块

    为了缩减篇幅,该模块中的方法实现暂时pass,pass掉也不耽误大家对整个流程的理解,如果感觉不顺眼的同学,可以结合着demo进行查看。

    class RedisClient(object):
        def __init__(self, host=REDIS_HOST, port=REDIS_PORT, password=REDIS_PASSWORD):
            """ 初始化 键值存储数据库 """
            pass
    
        def exists(self, proxy):
            """ 判断有序集合中是否存在代理 """
            pass
    
        def add(self, proxy, score=INITIAL_SCORE):
            """ 添加代理,并初始化分数 """
            pass
    
        def batch(self, start, stop):
            """ 批量获取代理 """
            pass
    
        def all(self):
            """  获取全部代理 """
            pass
    
        def count(self):
            """ 获取数量  """
            pass
    
        def max(self, proxy):
            """ 如果代理可用,将代理分数设置为最高 """
            pass
    
        def decrease(self, proxy):
            """ 将代理分数减去一分,小于最小值,则删除 """
            pass
    
        def random(self):
            """ 随机获取有效代理,首先尝试获取最高分代理,如果不存在,按照排名获取  """
            pass
    
    

      这个模块中主要是对Redis的使用,具体使用的是Redis中的有序集合,想进一步了解Redis的同学可以自行百度。demo中也有各个方法的详细说明,再次不再细说。

      本篇文章暂时介绍以上两个模块,剩下的模块定时更新。

    相关文章

      网友评论

        本文标题:‘人生苦短、我用Python’ 之 Redis+Flask维护免

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