我们从代理商买了代理后,代理商提供的接口返回的代理其实可用率还是值得深思的。如果你有钱,买的是代理商自建的代理,那可用率很高,也就不需要使用代理池筛选了。如果像我这样的学生党,只能买得起测试级别的代理,这种代理一般是代理商扫描出来的,可用时间和可用率可想而知,这样就需要一个代理池筛选出有用的代理并提供接口给爬虫。
既然只是为了测试代理的可用性,那么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()原文博客:https://blog.csdn.net/Qwertyuiop2016/ 现在这个是小号,给爬虫用的
欢迎关注我的微信公众号,分享我的学习经验和一些觉得不错的资源。如果有问题的话,也可以在微信公众号联系我。