如何维护Python3爬虫中的高效代理池?

2026-05-21 22:412阅读0评论SEO问题
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计2334个文字,预计阅读时间需要10分钟。

如何维护Python3爬虫中的高效代理池?

我们在上一节了解了代理的设置方法,通过代理我们可以解决目标网站封IP的问题。网上有大量免费的代理,部分可以直接使用,或购买付费代理。

我们在上一节了解了代理的设置方法,利用代理我们可以解决目标网站封 IP 的问题,而在网上又有大量公开的免费代理,其中有一部分可以拿来使用,或者我们也可以购买付费的代理 IP,价格也不贵。但是不论是免费的还是付费的,都不能保证它们每一个都是可用的,毕竟可能其他人也可能在用此 IP 爬取同样的目标站点而被封禁,或者代理服务器突然出故障或网络繁忙。一旦我们选用了一个不可用的代理,势必会影响我们爬虫的工作效率。

所以说,在用代理时,我们需要提前做一下筛选,将不可用的代理剔除掉,保留下可用代理,接下来在获取代理时从可用代理里面取出直接使用就好了。

所以本节我们来搭建一个高效易用的代理池。

1. 准备工作

要实现代理池我们首先需要成功安装好了 Redis 数据库并启动服务,另外还需要安装 Aiowww.66ip.cn/{}.html' urls=[start_url.format(page)forpageinrange(1,page_count+1)] forurlinurls: print('Crawling',url) html=get_page(url) ifhtml: doc=pq(html) trs=doc('.containerboxtabletr:gt(0)').items() fortrintrs: ip=tr.find('td:nth-child(1)').text() port=tr.find('td:nth-child(2)').text() yield':'.join([ip,port]) defcrawl_proxy360(self): """ 获取Proxy360 :return:代理 """ start_url='www.proxy360.cn/Region/China' print('Crawling',start_url) html=get_page(start_url) ifhtml: doc=pq(html) lines=doc('div[name="list_proxy_ip"]').items() forlineinlines: ip=line.find('.tbBottomLine:nth-child(1)').text() port=line.find('.tbBottomLine:nth-child(2)').text() yield':'.join([ip,port]) defcrawl_goubanjia(self): """ 获取Goubanjia :return:代理 """ start_url='www.goubanjia.com/free/gngn/index.shtml' html=get_page(start_url) ifhtml: doc=pq(html) tds=doc('td.ip').items() fortdintds: td.find('p').remove() yieldtd.text().replace('','')

为了实现灵活,在这里我们将获取代理的一个个方法统一定义一个规范,如统一定义以 crawl 开头,这样扩展的时候只需要添加 crawl 开头的方法即可。

在这里实现了几个示例,如抓取代理 66、Proxy360、Goubanjia 三个免费代理网站,这些方法都定义成了生成器,通过 yield 返回一个个代理。首先将网页获取,然后用PyQuery 解析,解析出IP加端口的形式的代理然后返回。

然后定义了一个 get_proxies() 方法,将所有以 crawl 开头的方法调用一遍,获取每个方法返回的代理并组合成列表形式返回。

你可能会想知道是怎样获取了所有以 crawl 开头的方法名称的。其实这里借助于元类来实现,定义了一个 ProxyMetaclass,Crawl 类将它设置为元类,元类中实现了 new() 方法,这个方法有固定的几个参数,其中第四个参数 attrs 中包含了类的一些属性,这其中就包含了类中方法的一些信息,我们可以遍历 attrs 这个变量即可获取类的所有方法信息。所以在这里我们在 new() 方法中遍历了 attrs 的这个属性,就像遍历一个字典一样,键名对应的就是方法的名称,接下来判断其开头是否是 crawl,如果是,则将其加入到 CrawlFunc 属性中,这样我们就成功将所有以 crawl 开头的方法定义成了一个属性,就成功动态地获取到所有以 crawl 开头的方法列表了。

所以说,如果要做扩展的话,我们只需要添加一个以 crawl开头的方法,例如抓取快代理,我们只需要在 Crawler 类中增加 crawl_kuaidaili() 方法,仿照其他的几个方法将其定义成生成器,抓取其网站的代理,然后通过 yield 返回代理即可,所以这样我们可以非常方便地扩展,而不用关心类其他部分的实现逻辑。

代理网站的添加非常灵活,不仅可以添加免费代理,也可以添加付费代理,一些付费代理的提取方式其实也类似,也是通过 Web 的形式获取,然后进行解析,解析方式可能更加简单,如解析纯文本或 Json,解析之后以同样的方式返回即可,在此不再添加,可以自行扩展。

既然定义了这个 Crawler 类,我们就要调用啊,所以在这里再定义一个 Getter 类,动态地调用所有以 crawl 开头的方法,然后获取抓取到的代理,将其加入到数据库存储起来。

fromdbimportRedisClient fromcrawlerimportCrawler POOL_UPPER_THRESHOLD=10000 classGetter(): def__init__(self): self.redis=RedisClient() self.crawler=Crawler() defis_over_threshold(self): """ 判断是否达到了代理池限制 """ ifself.redis.count()>=POOL_UPPER_THRESHOLD: returnTrue else: returnFalse defrun(self): print('获取器开始执行') ifnotself.is_over_threshold(): forcallback_labelinrange(self.crawler.__CrawlFuncCount__): callback=self.crawler.__CrawlFunc__[callback_label] proxies=self.crawler.get_proxies(callback) forproxyinproxies: self.redis.add(proxy)

Getter 类就是获取器类,这其中定义了一个变量 POOL_UPPER_THRESHOLD 表示代理池的最大数量,这个数量可以灵活配置,然后定义了 is_over_threshold() 方法判断代理池是否已经达到了容量阈值,它就是调用了 RedisClient 的 count() 方法获取代理的数量,然后加以判断,如果数量达到阈值则返回 True,否则 False。如果不想加这个限制可以将此方法永久返回 True。

接下来定义了 run() 方法,首先判断了代理池是否达到阈值,然后在这里就调用了 Crawler 类的 CrawlFunc 属性,获取到所有以 crawl 开头的方法列表,依次通过 get_proxies() 方法调用,得到各个方法抓取到的代理,然后再利用 RedisClient 的 add() 方法加入数据库,这样获取模块的工作就完成了。

检测模块

如何维护Python3爬虫中的高效代理池?

在获取模块中,我们已经成功将各个网站的代理获取下来了,然后就需要一个检测模块来对所有的代理进行一轮轮的检测,检测可用就设置为 100,不可用就分数减 1,这样就可以实时改变每个代理的可用情况,在获取有效代理的时候只需要获取分数高的代理即可。

由于代理的数量非常多,为了提高代理的检测效率,我们在这里使用异步请求库 Aiowww.baidu.com' BATCH_TEST_SIZE=100 classTester(object): def__init__(self): self.redis=RedisClient() asyncdeftest_single_proxy(self,proxy): """ 测试单个代理 :paramproxy:单个代理 :return:None """ conn=aio'+proxy print('正在测试',proxy) asyncwithsession.get(TEST_URL,proxy=real_proxy,timeout=15)asresponse: ifresponse.statusinVALID_STATUS_CODES: self.redis.max(proxy) print('代理可用',proxy) else: self.redis.decrease(proxy) print('请求响应码不合法',proxy) except(ClientError,ClientConnectorError,TimeoutError,AttributeError): self.redis.decrease(proxy) print('代理请求失败',proxy) defrun(self): """ 测试主函数 :return:None """ print('测试器开始运行') try: proxies=self.redis.all() loop=asyncio.get_event_loop() #批量测试 foriinrange(0,len(proxies),BATCH_TEST_SIZE): test_proxies=proxies[i:i+BATCH_TEST_SIZE] tasks=[self.test_single_proxy(proxy)forproxyintest_proxies] loop.run_until_complete(asyncio.wait(tasks)) time.sleep(5) exceptExceptionase: print('测试器发生错误',e.args)

在这里定义了一个类 Tester,init() 方法中建立了一个 RedisClient 对象,供类中其他方法使用。接下来定义了一个 test_single_proxy() 方法,用来检测单个代理的可用情况,其参数就是被检测的代理,注意这个方法前面加了 async 关键词,代表这个方法是异步的,方法内部首先创建了 Aioaio127.0.0.1:5555,即可看到其首页,如图 9-3 所示:

图 9-3 首页页面

再访问:127.0.0.1:5555/random,即可获取随机可用代理,如图 9-4 所示:

图 9-4 获取代理页面

所以后面我们只需要访问此接口即可获取一个随机可用代理,非常方便。

获取代理的代码如下:

importrequests PROXY_POOL_URL='localhost:5555/random' defget_proxy(): try: response=requests.get(PROXY_POOL_URL) ifresponse.status_code==200: returnresponse.text exceptConnectionError: returnNone

获取下来之后便是一个字符串类型的代理,可以按照上一节所示的方法设置代理,如 Requests 的使用方法如下:

importrequests proxy=get_proxy() proxies={ ''+proxy, ''+proxy, } try: response=requests.get('github.com/Python3WebSpider/ProxyPool。

7. 结语

本节我们实现了一个比较高效的代理池来获取随机可用的代理,整个内容比较多,需要好好理解一下。

在后文我们会利用代理池来实现数据的抓取。

以上就是Python3爬虫关于代理池的维护详解的详细内容,更多关于Python3爬虫代理池维护的资料请关注易盾网络其它相关文章!

本文共计2334个文字,预计阅读时间需要10分钟。

如何维护Python3爬虫中的高效代理池?

我们在上一节了解了代理的设置方法,通过代理我们可以解决目标网站封IP的问题。网上有大量免费的代理,部分可以直接使用,或购买付费代理。

我们在上一节了解了代理的设置方法,利用代理我们可以解决目标网站封 IP 的问题,而在网上又有大量公开的免费代理,其中有一部分可以拿来使用,或者我们也可以购买付费的代理 IP,价格也不贵。但是不论是免费的还是付费的,都不能保证它们每一个都是可用的,毕竟可能其他人也可能在用此 IP 爬取同样的目标站点而被封禁,或者代理服务器突然出故障或网络繁忙。一旦我们选用了一个不可用的代理,势必会影响我们爬虫的工作效率。

所以说,在用代理时,我们需要提前做一下筛选,将不可用的代理剔除掉,保留下可用代理,接下来在获取代理时从可用代理里面取出直接使用就好了。

所以本节我们来搭建一个高效易用的代理池。

1. 准备工作

要实现代理池我们首先需要成功安装好了 Redis 数据库并启动服务,另外还需要安装 Aiowww.66ip.cn/{}.html' urls=[start_url.format(page)forpageinrange(1,page_count+1)] forurlinurls: print('Crawling',url) html=get_page(url) ifhtml: doc=pq(html) trs=doc('.containerboxtabletr:gt(0)').items() fortrintrs: ip=tr.find('td:nth-child(1)').text() port=tr.find('td:nth-child(2)').text() yield':'.join([ip,port]) defcrawl_proxy360(self): """ 获取Proxy360 :return:代理 """ start_url='www.proxy360.cn/Region/China' print('Crawling',start_url) html=get_page(start_url) ifhtml: doc=pq(html) lines=doc('div[name="list_proxy_ip"]').items() forlineinlines: ip=line.find('.tbBottomLine:nth-child(1)').text() port=line.find('.tbBottomLine:nth-child(2)').text() yield':'.join([ip,port]) defcrawl_goubanjia(self): """ 获取Goubanjia :return:代理 """ start_url='www.goubanjia.com/free/gngn/index.shtml' html=get_page(start_url) ifhtml: doc=pq(html) tds=doc('td.ip').items() fortdintds: td.find('p').remove() yieldtd.text().replace('','')

为了实现灵活,在这里我们将获取代理的一个个方法统一定义一个规范,如统一定义以 crawl 开头,这样扩展的时候只需要添加 crawl 开头的方法即可。

在这里实现了几个示例,如抓取代理 66、Proxy360、Goubanjia 三个免费代理网站,这些方法都定义成了生成器,通过 yield 返回一个个代理。首先将网页获取,然后用PyQuery 解析,解析出IP加端口的形式的代理然后返回。

然后定义了一个 get_proxies() 方法,将所有以 crawl 开头的方法调用一遍,获取每个方法返回的代理并组合成列表形式返回。

你可能会想知道是怎样获取了所有以 crawl 开头的方法名称的。其实这里借助于元类来实现,定义了一个 ProxyMetaclass,Crawl 类将它设置为元类,元类中实现了 new() 方法,这个方法有固定的几个参数,其中第四个参数 attrs 中包含了类的一些属性,这其中就包含了类中方法的一些信息,我们可以遍历 attrs 这个变量即可获取类的所有方法信息。所以在这里我们在 new() 方法中遍历了 attrs 的这个属性,就像遍历一个字典一样,键名对应的就是方法的名称,接下来判断其开头是否是 crawl,如果是,则将其加入到 CrawlFunc 属性中,这样我们就成功将所有以 crawl 开头的方法定义成了一个属性,就成功动态地获取到所有以 crawl 开头的方法列表了。

所以说,如果要做扩展的话,我们只需要添加一个以 crawl开头的方法,例如抓取快代理,我们只需要在 Crawler 类中增加 crawl_kuaidaili() 方法,仿照其他的几个方法将其定义成生成器,抓取其网站的代理,然后通过 yield 返回代理即可,所以这样我们可以非常方便地扩展,而不用关心类其他部分的实现逻辑。

代理网站的添加非常灵活,不仅可以添加免费代理,也可以添加付费代理,一些付费代理的提取方式其实也类似,也是通过 Web 的形式获取,然后进行解析,解析方式可能更加简单,如解析纯文本或 Json,解析之后以同样的方式返回即可,在此不再添加,可以自行扩展。

既然定义了这个 Crawler 类,我们就要调用啊,所以在这里再定义一个 Getter 类,动态地调用所有以 crawl 开头的方法,然后获取抓取到的代理,将其加入到数据库存储起来。

fromdbimportRedisClient fromcrawlerimportCrawler POOL_UPPER_THRESHOLD=10000 classGetter(): def__init__(self): self.redis=RedisClient() self.crawler=Crawler() defis_over_threshold(self): """ 判断是否达到了代理池限制 """ ifself.redis.count()>=POOL_UPPER_THRESHOLD: returnTrue else: returnFalse defrun(self): print('获取器开始执行') ifnotself.is_over_threshold(): forcallback_labelinrange(self.crawler.__CrawlFuncCount__): callback=self.crawler.__CrawlFunc__[callback_label] proxies=self.crawler.get_proxies(callback) forproxyinproxies: self.redis.add(proxy)

Getter 类就是获取器类,这其中定义了一个变量 POOL_UPPER_THRESHOLD 表示代理池的最大数量,这个数量可以灵活配置,然后定义了 is_over_threshold() 方法判断代理池是否已经达到了容量阈值,它就是调用了 RedisClient 的 count() 方法获取代理的数量,然后加以判断,如果数量达到阈值则返回 True,否则 False。如果不想加这个限制可以将此方法永久返回 True。

接下来定义了 run() 方法,首先判断了代理池是否达到阈值,然后在这里就调用了 Crawler 类的 CrawlFunc 属性,获取到所有以 crawl 开头的方法列表,依次通过 get_proxies() 方法调用,得到各个方法抓取到的代理,然后再利用 RedisClient 的 add() 方法加入数据库,这样获取模块的工作就完成了。

检测模块

如何维护Python3爬虫中的高效代理池?

在获取模块中,我们已经成功将各个网站的代理获取下来了,然后就需要一个检测模块来对所有的代理进行一轮轮的检测,检测可用就设置为 100,不可用就分数减 1,这样就可以实时改变每个代理的可用情况,在获取有效代理的时候只需要获取分数高的代理即可。

由于代理的数量非常多,为了提高代理的检测效率,我们在这里使用异步请求库 Aiowww.baidu.com' BATCH_TEST_SIZE=100 classTester(object): def__init__(self): self.redis=RedisClient() asyncdeftest_single_proxy(self,proxy): """ 测试单个代理 :paramproxy:单个代理 :return:None """ conn=aio'+proxy print('正在测试',proxy) asyncwithsession.get(TEST_URL,proxy=real_proxy,timeout=15)asresponse: ifresponse.statusinVALID_STATUS_CODES: self.redis.max(proxy) print('代理可用',proxy) else: self.redis.decrease(proxy) print('请求响应码不合法',proxy) except(ClientError,ClientConnectorError,TimeoutError,AttributeError): self.redis.decrease(proxy) print('代理请求失败',proxy) defrun(self): """ 测试主函数 :return:None """ print('测试器开始运行') try: proxies=self.redis.all() loop=asyncio.get_event_loop() #批量测试 foriinrange(0,len(proxies),BATCH_TEST_SIZE): test_proxies=proxies[i:i+BATCH_TEST_SIZE] tasks=[self.test_single_proxy(proxy)forproxyintest_proxies] loop.run_until_complete(asyncio.wait(tasks)) time.sleep(5) exceptExceptionase: print('测试器发生错误',e.args)

在这里定义了一个类 Tester,init() 方法中建立了一个 RedisClient 对象,供类中其他方法使用。接下来定义了一个 test_single_proxy() 方法,用来检测单个代理的可用情况,其参数就是被检测的代理,注意这个方法前面加了 async 关键词,代表这个方法是异步的,方法内部首先创建了 Aioaio127.0.0.1:5555,即可看到其首页,如图 9-3 所示:

图 9-3 首页页面

再访问:127.0.0.1:5555/random,即可获取随机可用代理,如图 9-4 所示:

图 9-4 获取代理页面

所以后面我们只需要访问此接口即可获取一个随机可用代理,非常方便。

获取代理的代码如下:

importrequests PROXY_POOL_URL='localhost:5555/random' defget_proxy(): try: response=requests.get(PROXY_POOL_URL) ifresponse.status_code==200: returnresponse.text exceptConnectionError: returnNone

获取下来之后便是一个字符串类型的代理,可以按照上一节所示的方法设置代理,如 Requests 的使用方法如下:

importrequests proxy=get_proxy() proxies={ ''+proxy, ''+proxy, } try: response=requests.get('github.com/Python3WebSpider/ProxyPool。

7. 结语

本节我们实现了一个比较高效的代理池来获取随机可用的代理,整个内容比较多,需要好好理解一下。

在后文我们会利用代理池来实现数据的抓取。

以上就是Python3爬虫关于代理池的维护详解的详细内容,更多关于Python3爬虫代理池维护的资料请关注易盾网络其它相关文章!