异步代理池

前言

我们从代理商买了代理后,代理商提供的接口返回的代理其实可用率还是值得深思的。如果你有钱,买的是代理商自建的代理,那可用率很高,也就不需要使用代理池筛选了。如果像我这样的学生党,只能买得起测试级别的代理,这种代理一般是代理商扫描出来的,可用时间和可用率可想而知,这样就需要一个代理池筛选出有用的代理并提供接口给爬虫。

既然只是为了测试代理的可用性,那么asyncio+aiohttp再合适不过了(效率高,代码简单)。

思路

创建两个Redis集合,一个存储提取的代理,一个存储有效的代理,然后每隔一段时间请求一次接口得到若干个代理,这个一段时间要看你的代理商的限制和你爬虫的需求,将得到的代理放入到总代理池,并交给程序验证,将有效的代理存入有效的池中,当下一次提取数据的时候,我先验证得到的代理在不在总代理池中,如果在则不验证,不在的话再验证其有效性,另外,每隔一段时间,验证一下总代理的代理是否可用(针对网上扫描的不稳定代理),不验证有效的代理池的代理是否可用,这个交给爬虫程序去验证。当然,以上的思路只是针对不稳定代理的,如果是代理商自建的代理,需要做一些改动提高代理池的可用率。

小改进

本来想开发一个接口给爬虫,但想了想,接口也是要从数据库中取出来,我为什么不让爬虫直接从数据库中取呢,这样不是更节省时间,提高效率。

小思考

这里出现了一个小的问题,验证的网站是要选择爬虫爬取的网站(后面称目标网站),还是选择其他的网站呢,如果选择目标网站,那么可不可能爬虫和代理程序同时在使用一个代理,多次之后,造成代理的被封呢。如果选择其他网站,目标网站已经封了这个代理也说不定。最后,我还是选择了目标网站,因为买的是万人骑的代理,那么验证目标网站的可用性就显得很重要了。自建的优质代理则可以选择一些其他网站。

问题

代理的使用间隔。假如我们有效代理池中有100个代理,而爬虫程序应该开多少个协程比较合适呢?对于我买的这个代理,我试过过度消费代理,就是即使只有100个代理,但我依旧开1000个协程去爬取,这样会导致一个代理被使用多次后失效,然后在某个时间点,我的代理池是空的,爬虫就停止了。后面我才学会将协程的数量调整到代理数量*(1.5-2),因为我爬取的网站限制不是很严格,一秒请求两次是被允许的。于是爬虫和代理池才都能稳定运行。但成功率依旧不高,80%左右,有时甚至只有60%,但这样得到的数据居然没有过度消费代理来的多,我能怎么办,我也很无奈啊。

代码:

# -*- coding: utf-8 -*-
import asyncio
import aiohttp
import time
import json
import redis
import requests


class GetProxies:
    def __init__(self):
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.61 Safari/537.36',
        }
        self.url = '' # 用于验证代理的URL
        self.r = redis.Redis(decode_responses=True)
        self.rset = 'sproxies' # redis集合, 存储已提取的代理
        
    def start(self):
        self.vset = 'vproxies' # redis集合, 存储有效的代理
        n = 0 # 用于间隔标识,每十次检验一次sproxies中代理是否有效
        while True:
            n += 1
            if n == 10:
                print('--开始验证已获取代理--')
                self.test_proxy()
                n = 0
            print('---开始获取代理---')
            self.get_proxy()
            print('---获取完成,代理池数量:(%d)---' % self.r.scard(self.vset))
            time.sleep(5)
            
    def test_proxy(self):
        '''
            取出sproxies中代理,检验其有效性
        '''
        proxy_list = self.r.smembers(self.rset) 
        self.run(proxy_list)
        
    def get_proxy(self):
        '''
            这个函数有点多余,不过我喜欢这样写
        '''
        proxy_list = self.get_api()
        if proxy_list:
            self.r.sadd(self.rset, *proxy_list)
            self.run(proxy_list)
        
    def get_api(self):
        '''
            获取代理(http://127.0.0.1:12306),返回列表
        '''
        pass
    
    
    async def get(self, proxy, session):
        '''
            协程函数,每个协程运行的函数
        '''
        try:
            async with session.get(self.url, timeout=10, proxy=proxy) as resp:
                if resp.status == 200:
                    self.r.sadd(self.vset, proxy)
        # 这三个异常都是代理失效造成的            
        except (aiohttp.ClientError, aiohttp.client_exceptions.ClientConnectorError, asyncio.TimeoutError):
            pass
        
       
    
    async def main(self, proxy_list):
        '''
            所有协程
        '''
        tasks = []
        async with asyncio.Semaphore(500):
            session = aiohttp.ClientSession(headers=self.headers)
            for proxy in proxy_list:
                task = asyncio.ensure_future(self.get(proxy, session))
                tasks.append(task)
            await asyncio.wait(tasks)
            await session.close()
        
    def run(self, proxy_list):
        '''
            运行协程
        '''
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        loop.run_until_complete(self.main(proxy_list))
        loop.run_until_complete(asyncio.sleep(0))
        loop.close()
        
           
        
if __name__ == '__main__':
    g = GetProxies()
    g.start()
        

猜你喜欢

转载自blog.csdn.net/Qwertyuiop2016/article/details/89338731