前文已经完成对代理池Redis数据库模块的设计开发,本节将进行Getter模块的设计开发。Getter模块我们已经介绍过,其负责从各个代理源网站获取最新代理的存入代理池。

设计思路

  • 我们需要获取代理源的网站可能会经常变动,针对每个代理源网站的获取方式都是不同的,所以我们设计一个Crawler类,每个代理网站中代理源的获取,作为一个方法,并将其用一定规则进行命名,符合规则命名的方法,将被认为是代理源获取,而被动态调用。

  • Crawler职责很明显,单纯负责代理源获取,作为Getter模块的一部分被使用,获取完代理源后,Getter模块其他部分进行入池等操作。

开发

在MyProxyPool项目中新建getter.py:

from pyquery import PyQuery as pq
import requests
from redisdb import RedisCli

# 基础请求头
BASE_HEADERS = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36',
    'Accept-Encoding': 'gzip, deflate, sdch',
    'Accept-Language': 'en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7'
}

# 代理池最大数量
POOL_UPPER_THRESHOLD = 10000


class Crawler:
    def get_proxies(self, crawl_name):
        '''
        通过代理爬取名获取代理 如crawl_dailli66
        :param crawl_name: 爬取代理的方法名
        :return: 代理
        '''
        proxies = []
        for proxy in eval('self.%s()' % crawl_name):
            # 获取代理成
            proxies.append(proxy)
        return proxies

    def get_page(self, url, options={}):
        """
        获取网页
        :param url:
        :param options:
        :return:
        """
        headers = dict(BASE_HEADERS, **options)
        print('正在抓取', url)
        try:
            response = requests.get(url, headers=headers)
            print('抓取成功', url, response.status_code)
            if response.status_code == 200:
                return response.text
        except Exception as e:
            print('抓取失败', e, url)
        return None

    def get_all_crawl_func(self):
        '''
        获取所有爬取代理源的方法
        :return:
        '''
        funcs = []
        for attr in dir(self):
            if attr.startswith('crawl_'):
                funcs.append(attr)
        return funcs

    def crawl_dailli66(self, page_count=4):
        '''
        爬取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:
            print('爬取', url)
            html = self.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])

    # ...爬取其他网站的


class Getter:
    def __init__(self):
        # 初始化代理池和代理源爬取
        self.redis = RedisCli()
        self.crawler = Crawler()

    def is_over_thershold(self):
        '''
        判断是否达到代理池最大限制
        :return: 是否达到代理池最大限制
        '''
        return self.redis.count() >= POOL_UPPER_THRESHOLD

    def run(self):
        '''
        获取代理源 存入代理池
        :return:
        '''
        if not self.is_over_thershold():
            for crawl_fun in self.crawler.get_all_crawl_func():
                proxies = self.crawler.get_proxies(crawl_fun)
                for proxy in proxies:
                    self.redis.add(proxy)


if __name__ == '__main__':
    # 执行测试
    # print(Crawler().get_proxies('crawl_dailli66'))
    Getter().run()


Crawler中,我们定义了一个规则,所有以crawl_开头的方法,我们就认为这是一个代理源获取方法,调用get_all_crawl_func就可以获取到所有代理源方法,然后通过get_proxies来进行一个个调用进行代理源获取。如果我们又新的代理源获取网站需要加入,那么我们只需要新增一个crawl_开头的代理源获取方法即可,无需改动其他任何逻辑。(这里我只写了一个66免费代理网用作演示)

Getter中,实例化代理源爬取对象Crawler和redis代理池操作对象RedisCli,然后进行代理源爬取和入池。

代理池中存放的代理数量应该是有限的,不应该无限制的存入,所以我们设置了一个代理池最大数量POOL_UPPER_THRESHOLD = 10000,当代理池中代理数量小于10000时,我们才进行代理源获取存入代理池。

本节代码戳我

微信 OR 支付宝 扫描二维码
给复仇者码农 打个赏吧  
微信打赏   支付宝打赏  
金额随意 快来“打”我呀~

发表评论