【Python3爬虫】教你怎么利用免费代理搭建代理池

一、写在前面

有时候你的爬虫刚开始的时候可以正常运行,能够正常的爬取数据,但是过了一会,却出现了一个“403 Forbidden",或者是”您的IP访问频率太高“这样的提示,这就意味着你的IP被ban了,好一点的情况是过一段时间你就能继续爬取了,坏一点的情况就是你的IP已经进入别人的黑名单了,然后你的爬虫就GG了。怎么办呢?我们可以通过设置代理来解决,付费代理的效果自然不必多说,但是对于学习阶段的人来说,我觉得爬取网上的免费代理来用是一个更好的选择,而这一篇博客就将教你怎么利用免费代理搭建属于你自己的代理池。

二、目标分析

要搭建一个代理池,需要三个模块:存储模块、爬取模块和测试模块。

存储模块:负责存储我们爬取下来的代理,首先我们需要保证这些代理不能有重复的,然后我们还要对代理是否可用进行标记,这里可以使用Redis数据库的SortedSet(有序集合)进行存储。

爬取模块:负责对一些提供免费代理的网站进行爬取,代理的形式是IP+端口,爬取下来之后保存到数据库里。

测试模块:负责对代理池中的代理的可用性进行测试,由于测试出错不一定就表明代理不可用,可能是因为网络问题或者请求超时等等,所以我们可以设置一个分数标识,100分标识可用,分数越低标识可用性越低,当分数低于一个阈值之后,就从代理池中移除。

三、具体实现

1、存储模块

这里使用的是Redis的有序集合。Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员,不同的是每个元素都会关联一个double类型的分数,Redis正是通过分数来为集合中的成员进行从小到大的排序。这样我们保存到数据库中的元素就是一个代理和一个分数,比如112.17.65.133:8060和100,这就表示112.17.65.133:8060这个代理是可用的。

对于新添加到数据库中的代理,设置的初始分数为10,添加之后会进行一次测试,如果可用就把分数改为100表明可用,如果测试的结果是不可用就把分数减1,当分数减到0后就从代理池中移除。这么做的意义在于一次测试不可用并不能代表这个代理完全不可用,有可能在之后的某次测试中是可用的,这样我们就减小了将一个原本可用的代理从代理池中移除出去的概率。

当我们想要从代理池中获取一个代理的时候,优先从分数为100的代理中随机获取,如果一个100分的代理都没有,则对所有代理进行排序,然后随机获取一个代理。由于我们使用的是随机获取,这样就能保证代理池中的所有代理都有被获取的可能性。

现在我们需要定义一个类来实现这个有序集合,还需要设置一些方法来实现添加代理、修改分数、获取代理等功能。具体代码如下:

  1 """
  2 Version: Python3.5
  3 Author: OniOn
  4 Site: http://www.cnblogs.com/TM0831/
  5 Time: 2019/2/12 14:54
  6 """
  7 import redis
  8 import random
  9
 10 MAX_SCORE = 100  # 最高分
 11 MIN_SCORE = 0  # 最低分
 12 INITIAL_SCORE = 10  # 初始分数
 13 REDIS_HOST = "localhost"
 14 REDIS_PORT = 6379
 15
 16
 17 class RedisClient:
 18     def __init__(self):
 19         self.db = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, db=0)
 20         self.key = "proxies"
 21
 22     def add(self, proxy, score=INITIAL_SCORE):
 23         """
 24         将代理添加到代理池中
 25         :param proxy: 代理
 26         :param score: 分数
 27         :return:
 28         """
 29         if not self.is_exist(proxy):
 30             self.db.zadd(self.key, proxy, score)
 31
 32     def is_exist(self, proxy):
 33         """
 34         判断代理池中是否存在该代理
 35         :param proxy: 代理
 36         :return: True or False
 37         """
 38         if self.db.zscore(self.key, proxy):
 39             return True
 40         else:
 41             return False
 42
 43     def random(self):
 44         """
 45         获取有效代理,先获取最高分代理,如果不存在,则按分数排名然后随机获取
 46         :return: 代理
 47         """
 48         result = self.db.zrangebyscore(self.key, MAX_SCORE, MAX_SCORE)
 49         if len(result):
 50             return random.choice(result)
 51         else:
 52             result = self.db.zrangebyscore(self.key, MIN_SCORE, MAX_SCORE)
 53             if len(result):
 54                 return random.choice(result)
 55             else:
 56                 print("代理池已空!")
 57
 58     def decrease(self, proxy):
 59         """
 60         代理分数减1分,若小于最低分,则从代理池中移除
 61         :param proxy:
 62         :return:
 63         """
 64         if self.is_exist(proxy):
 65             score = self.db.zscore(self.key, proxy)
 66             if score > MIN_SCORE:
 67                 score -= 1
 68                 self.db.zadd(self.key, proxy, score)
 69             else:
 70                 self.delete(proxy)
 71
 72     def max(self, proxy):
 73         """
 74         将代理分数设置为最高分
 75         :param proxy: 代理
 76         :return:
 77         """
 78         if self.is_exist(proxy):
 79             self.db.zadd(self.key, proxy, MAX_SCORE)
 80
 81     def delete(self, proxy):
 82         """
 83         从代理池中移除该代理
 84         :param proxy: 代理
 85         :return:
 86         """
 87         if self.is_exist(proxy):
 88             self.db.zrem(self.key, proxy)
 89
 90     def all(self):
 91         """
 92         获取代理池中的所有代理
 93         :return:
 94         """
 95         if self.count():
 96             return self.db.zrange(self.key, MIN_SCORE, MAX_SCORE)
 97
 98     def count(self):
 99         """
100         获取代理池中代理数量
101         :return:
102         """
103         return self.db.zcard(self.key)

2、爬取模块

爬取模块比较简单,就是定义一个Crawler类来对一些提供免费代理的网站进行爬取。具体代码如下:

 1 """
 2 Version: Python3.5
 3 Author: OniOn
 4 Site: http://www.cnblogs.com/TM0831/
 5 Time: 2019/2/12 15:07
 6 """
 7 import requests
 8 from lxml import etree
 9 from fake_useragent import UserAgent
10
11
12 # 设置元类
13 class CrawlMetaClass(type):
14     def __new__(cls, name, bases, attrs):
15         attrs[‘__CrawlFuncCount__‘] = 0
16         attrs[‘__CrawlFunc__‘] = []
17         for k, v in attrs.items():
18             if ‘crawl_‘ in k:
19                 attrs[‘__CrawlFunc__‘].append(k)
20                 attrs[‘__CrawlFuncCount__‘] += 1
21         # attrs[‘__CrawlFuncCount__‘] = count
22         return type.__new__(cls, name, bases, attrs)
23
24
25 class Crawler(object, metaclass=CrawlMetaClass):
26     def __init__(self):
27         self.proxies = []  # 代理列表
28         ua = UserAgent()  # 使用随机UA
29         self.headers = {
30             "UserAgent": ua.random
31         }
32
33     def get_proxies(self, callback):
34         """
35         运行各个代理爬虫
36         :param callback: crawl函数名称
37         :return:
38         """
39         for proxy in eval("self.{}()".format(callback)):
40             print("成功获取代理:", proxy)
41             self.proxies.append(proxy)
42         return self.proxies
43
44     def crawl_kdd(self):
45         """
46         快代理爬虫
47         :return:
48         """
49         urls = ["https://www.kuaidaili.com/free/inha/{}/".format(i) for i in range(1, 4)]
50         for url in urls:
51             res = requests.get(url, headers=self.headers)
52             try:
53                 et = etree.HTML(res.text)
54                 ip_list = et.xpath(‘//*[@data-title="IP"]/text()‘)
55                 port_list = et.xpath(‘//*[@data-title="PORT"]/text()‘)
56                 for ip, port in zip(ip_list, port_list):
57                     yield ip + ":" + port
58             except Exception as e:
59                 print(e)
60
61     def crawl_89ip(self):
62         """
63         89IP爬虫
64         :return:
65         """
66         urls = ["http://www.89ip.cn/index_{}.html".format(i) for i in range(1, 4)]
67         for url in urls:
68             res = requests.get(url, headers=self.headers)
69             try:
70                 et = etree.HTML(res.text)
71                 ip_list = et.xpath(‘//*[@class="layui-table"]/tbody/tr/td[1]/text()‘)
72                 port_list = et.xpath(‘//*[@class="layui-table"]/tbody/tr/td[2]/text()‘)
73                 ip_list = [i.strip() for i in ip_list]
74                 port_list = [i.strip() for i in port_list]
75                 for ip, port in zip(ip_list, port_list):
76                     yield ip + ":" + port
77             except Exception as e:
78                 print(e)
79
80     def crawl_xc(self):
81         """
82         西刺代理爬虫
83         :return:
84         """
85         url = "https://www.xicidaili.com/?t=253"
86         res = requests.get(url, headers=self.headers)
87         try:
88             et = etree.HTML(res.text)
89             ip_list = et.xpath(‘//*[@id="ip_list"]/tr[3]/td[2]/text()‘)
90             port_list = et.xpath(‘//*[@id="ip_list"]/tr[3]/td[3]/text()‘)
91             for ip, port in zip(ip_list, port_list):
92                 yield ip + ":" + port
93         except Exception as e:
94             print(e)

可以看到几个爬虫的方法名称都是以crawl开头的,主要是为了方便,如果要添加新的爬虫就只用添加crawl开头的方法即可。

这里我写了爬取快代理、89IP代理和西刺代理的爬虫,都是用xpath进行解析,也都定义了一个生成器,然后用yield返回爬取到的代理。然后定义了一个get_proxies()方法,将所有以crawl开头的方法都调用一遍,获取每个方法返回的结果并生成一个代理列表,最后返回这个代理列表。那么如何获取crawl开头的方法呢?这里借用了元类来实现。首先定义一个类CrawlMetaClass,然后实现了__new__()方法,这个方法的第四个参数attrs里包含了类的一些属性,所以我们可以遍历attrs中包含的信息,就像遍历一个字典一样,如果方法名以crawl开头,就将其添加到__CrawlFunc__中,这样我们就能获取crawl开头的方法了。

我们已经定义好了爬取的方法了,但是还需要定义一个类来执行这些方法,这里可以定义一个GetProxy类来实现爬取代理并保存到代理池中,具体代码如下:

 1 """
 2 Version: Python3.5
 3 Author: OniOn
 4 Site: http://www.cnblogs.com/TM0831/
 5 Time: 2019/2/14 12:19
 6 """
 7 from ProxyPool.crawl import Crawler
 8 from ProxyPool.pool import RedisClient
 9
10
11 class GetProxy:
12     def __init__(self):
13         self.crawler = Crawler()
14         self.redis = RedisClient()
15
16     def get_proxy(self):
17         """
18         运行爬虫爬取代理
19         :return:
20         """
21         print("[INFO]Crawl Start...")
22         count = 0
23         for callback_label in range(self.crawler.__CrawlFuncCount__):
24             callback = self.crawler.__CrawlFunc__[callback_label]
25             # 获取代理
26             proxies = self.crawler.get_proxies(callback)
27             for proxy in proxies:
28                 self.redis.add(proxy)
29             count += len(proxies)
30         print("此次爬取的代理数量为:{}".format(count))
31         print("[INFO]Crawl End...\n\n")

3、测试模块

我们已经将代理成功爬取下来并保存到代理池中了,但是我们还需要对代理的可用性进行测试。测试的方法就是使用requests库设置代理并发送请求,如果请求成功并且返回的状态码是200的话,就表明这个代理是可用的,然后我们就要将该代理的分数设置为100,反之如果出现请求失败、请求超时或者返回的状态码不是200的话, 就要将该代理的分数减1,如果分数等于0了,就要从代理池中移除。

这里可以定义一个TestProxy类来实现,具体代码如下:

 1 """
 2 Version: Python3.5
 3 Author: OniOn
 4 Site: http://www.cnblogs.com/TM0831/
 5 Time: 2019/2/14 14:24
 6 """
 7 import time
 8 import random
 9 import requests
10 from fake_useragent import UserAgent
11 from ProxyPool.crawl import Crawler
12 from ProxyPool.pool import RedisClient
13
14
15 class TestProxy:
16     def __init__(self):
17         self.crawler = Crawler()
18         self.redis = RedisClient()
19         ua = UserAgent()  # 使用随机UA
20         self.headers = {
21             "UserAgent": ua.random
22         }
23
24     def test(self):
25         """
26         测试函数,测试代理池中的代理
27         :return:
28         """
29         proxy_list = self.redis.all()
30         proxy_list = [i.decode(‘utf-8‘) for i in proxy_list]  # 字节型转字符串型
31
32         print("[INFO]Test Start...")
33         for proxy in proxy_list:
34             self.request(proxy)
35         print("[INFO]Test End...\n\n")
36
37     def request(self, proxy):
38         """
39         测试请求函数
40         :param proxy:
41         :return:
42         """
43         print("当前测试代理:{}   该代理分数为:{}".format(proxy, self.redis.db.zscore(self.redis.key, proxy)))
44         time.sleep(random.randint(1, 4))
45         try:
46             url = "https://www.baidu.com/"
47             proxies = {
48                 "https": "https://" + proxy
49             }
50             res = requests.get(url, headers=self.headers, proxies=proxies, timeout=5)
51
52             if res.status_code == 200:
53                 print("代理可用,分数设置为100")
54                 self.redis.max(proxy)
55             else:
56                 print("错误的请求状态码,分数减1")
57                 self.redis.decrease(proxy)
58         except:
59             print("代理请求失败,分数减1")
60             self.redis.decrease(proxy)

四、运行程序

这里我定义了一个Main类来实现,过程为先爬取代理,然后对代理池中的代理进行测试,最后从代理池中获取一个可用代理。具体代码如下:

 1 """
 2 Version: Python3.5
 3 Author: OniOn
 4 Site: http://www.cnblogs.com/TM0831/
 5 Time: 2019/2/14 14:26
 6 """
 7 from ProxyPool.pool import RedisClient
 8 from ProxyPool.get import GetProxy
 9 from ProxyPool.test import TestProxy
10
11
12 class Main:
13     def __init__(self):
14         self.gp = GetProxy()
15         self.tp = TestProxy()
16         self.db = RedisClient()
17
18     def run(self):
19         """
20         运行的主函数,先爬取代理,然后测试,最后获取一个有效代理
21         :return:
22         """
23         self.gp.get_proxy()
24         self.tp.test()
25         proxy = self.db.random()
26         proxy = proxy.decode(‘utf-8‘)
27         print("从代理池中取出的代理为:{}".format(proxy))
28
29
30 if __name__ == ‘__main__‘:
31     m = Main()
32     m.run()

运行结果截图如下:

完整代码已上传到GitHub

原文地址:https://www.cnblogs.com/TM0831/p/10375046.html

时间: 2024-10-06 05:29:47

【Python3爬虫】教你怎么利用免费代理搭建代理池的相关文章

用python3爬虫-教大家如何解决验证码的问题

Python爬虫-2018年-我破解天眼查和启信宝企业数据爬虫--破解反爬技术那些事情 最近在自己用python3+mongdb写了一套分布式多线程的天眼查爬虫系统,实现了对天眼查整个网站的全部数据各种维度的采集和存储,主要是为了深入学习爬虫技术使用,并且根据天眼查网页的数据结构建立了30个表来存储30个维度的数据,很多做技术的朋友在爬天眼查的时候会遇到以下几个问题,我把我的经历和解决方案分享给大家.(需要爬虫技术交流的朋友欢迎加我qq:2779571288) #大数据爬虫系统-主要包含这些维度

Python3网络爬虫(十一):爬虫黑科技之让你的爬虫程序更像人类用户的行为(代理IP池等)

原文链接: Jack-Cui,http://blog.csdn.net/c406495762 运行平台: Windows Python版本: Python3.x IDE: Sublime text3 1 前言 近期,有些朋友问我一些关于如何应对反爬虫的问题.由于好多朋友都在问,因此决定写一篇此类的博客.把我知道的一些方法,分享给大家.博主属于小菜级别,玩爬虫也完全是处于兴趣爱好,如有不足之处,还望指正. 在互联网上进行自动数据采集(抓取)这件事和互联网存在的时间差不多一样长.今天大众好像更倾向于

Python获取免费的可用代理

Python获取免费的可用代理 在使用爬虫多次爬取同一站点时,常常会被站点的ip反爬虫机制给禁掉,这时就能够通过使用代理来解决.眼下网上有非常多提供最新免费代理列表的站点.这些列表里非常多的代理主机是可用的,可是也有一些是不可用的,因此须要进一步筛选.利用Python能够非常方便地筛选出可用的代理列表. 以提供免费代理信息的站点IPCN 国家地区免费代理为例,这里给出一个爬取此站点上提供的代理信息并筛选可用代理主机的程序.主要用到requests和lxml,详细代码为: # -*- coding

【Python3爬虫】用Python中的队列来写爬虫

一.写在前面 当你看着你的博客的阅读量慢慢增加的时候,内心不禁有了些小激动,但是不得不吐槽一下--博客园并不会显示你的博客的总阅读量是多少.而这一篇博客就将教你怎么利用队列这种结构来编写爬虫,最终获取你的博客的总阅读量. 二.必备知识 队列是常用数据结构之一,在Python3中要用queue这个模块来实现.queue这个模块实现了三种队列: class queue.Queue(maxsize=0):FIFO队列(first in first out),先进先出,第一个进入队列的元素会第一个从队列

Python3爬虫实战:实战源码+博客讲解

Python Spider 贵有恒,何必三更起五更睡:最无益,只怕一日暴十寒. Python3爬虫实战:实战源码+博客讲解 个人网站 CSDN博客 CSDN爬虫专栏 学习交流群[328127489] 声明 代码.教程仅限于学习交流,请勿用于任何商业用途! 文章首发声明 文章在自己的个人网站首发,其他平台文章均属转发,如想获得最新更新进展,欢迎关注我的个人网站:http://cuijiahua.com/ 目录 爬虫小工具 文件下载小助手 爬虫实战 笔趣看小说下载 百度文库免费文章下载助手_rev1

Python3 爬虫(八) -- BeautifulSoup之再次爬取CSDN博文

序 我的Python3爬虫(五)博文使用utllib基本函数以及正则表达式技术实现了爬取csdn全部博文信息的任务. 链接:Python3 爬虫(五) -- 单线程爬取我的CSDN全部博文 上一篇,我们学习了BeautifulSoup这样一个优秀的Python库,必须有效利用起来.那么我们就利用BeautifulSoup4重新实现一次爬取csdn博文的任务. 由于我修改了博客配置,首页主题换了一下,我们基于新的主题查看网页,如下图所示: 同样的,确认要提取的信息,以及博文总页数. 分析网页源码

【Python3爬虫】拉勾网爬虫

一.思路分析: 在之前写拉勾网的爬虫的时候,总是得到下面这个结果(真是头疼),当你看到下面这个结果的时候,也就意味着被反爬了,因为一些网站会有相应的反爬虫措施,例如很多网站会检测某一段时间某个IP的访问次数,如果访问频率太快以至于看起来不像正常访客,它可能就会禁止这个IP的访问: 对于拉勾网,我们要找到职位信息的ajax接口倒是不难(如下图),问题是怎么不得到上面的结果. 要想我们的爬虫不被检测出来,我们可以使用代理IP,而网上有很多提供免费代理的网站,比如西刺代理.快代理.89免费代理等等,我

爬虫1 爬虫介绍, requests模块, 代理(正向代理,反向代理), 爬梨视频, 自动登录网站, HTTP协议复习

HTTP协议复习 参考:https://www.cnblogs.com/an-wen/p/11180076.html 1爬虫介绍 # 1 本质:模拟发送http请求(requests)---->解析返回数据(re,bs4,lxml,json)--->入库(redis,mysql,mongodb) # 2 app爬虫:本质一模一样 # 3 为什么python做爬虫最好:包多,爬虫框架:scrapy:性能很高的爬虫框架,爬虫界的django,大而全(爬虫相关的东西都集成了) # 4 百度,谷歌,就

利用360免费wifi搭建局域网让他人访问Oracle数据库

步骤很简答有 第一部:安装完360免费,点击运行 第二部:关闭本地计算机的防火墙(找了半天问题所在后来才发现防火墙没关..关于防火墙的作用以后地看看 还有子网掩码) 第三部:就是让他人计算机通过PSQL连接你的数据库了(要是不想配置服务名的话把服务名改写为ip地址/数据库名) ip地址可以通过查看ipconfig得到 或者查看本地连接 利用360免费wifi搭建局域网让他人访问Oracle数据库,布布扣,bubuko.com