一、写在前面

有时候你的爬虫刚开始的时候可以正常运行,能够正常的爬取数据,但是过了一会,却出现了一个“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.53 Author: OniOn4 Site: http://www.cnblogs.com/TM0831/5 Time: 2019/2/12 14:546 """

7 importredis8 importrandom9

10 MAX_SCORE = 100 #最高分

11 MIN_SCORE = 0 #最低分

12 INITIAL_SCORE = 10 #初始分数

13 REDIS_HOST = "localhost"

14 REDIS_PORT = 6379

15

16

17 classRedisClient: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 notself.is_exist(proxy):30 self.db.zadd(self.key, proxy, score)31

32 defis_exist(self, proxy):33 """

34 判断代理池中是否存在该代理35 :param proxy: 代理36 :return: True or False37 """

38 ifself.db.zscore(self.key, proxy):39 returnTrue40 else:41 returnFalse42

43 defrandom(self):44 """

45 获取有效代理,先获取最高分代理,如果不存在,则按分数排名然后随机获取46 :return: 代理47 """

48 result =self.db.zrangebyscore(self.key, MAX_SCORE, MAX_SCORE)49 iflen(result):50 returnrandom.choice(result)51 else:52 result =self.db.zrangebyscore(self.key, MIN_SCORE, MAX_SCORE)53 iflen(result):54 returnrandom.choice(result)55 else:56 print("代理池已空!")57

58 defdecrease(self, proxy):59 """

60 代理分数减1分,若小于最低分,则从代理池中移除61 :param proxy:62 :return:63 """

64 ifself.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 defmax(self, proxy):73 """

74 将代理分数设置为最高分75 :param proxy: 代理76 :return:77 """

78 ifself.is_exist(proxy):79 self.db.zadd(self.key, proxy, MAX_SCORE)80

81 defdelete(self, proxy):82 """

83 从代理池中移除该代理84 :param proxy: 代理85 :return:86 """

87 ifself.is_exist(proxy):88 self.db.zrem(self.key, proxy)89

90 defall(self):91 """

92 获取代理池中的所有代理93 :return:94 """

95 ifself.count():96 returnself.db.zrange(self.key, MIN_SCORE, MAX_SCORE)97

98 defcount(self):99 """

100 获取代理池中代理数量101 :return:102 """

103 return self.db.zcard(self.key)

2、爬取模块

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

1 """

2 Version: Python3.53 Author: OniOn4 Site: http://www.cnblogs.com/TM0831/5 Time: 2019/2/12 15:076 """

7 importrequests8 from lxml importetree9 from fake_useragent importUserAgent10

11

12 #设置元类

13 classCrawlMetaClass(type):14 def __new__(cls, name, bases, attrs):15 attrs['__CrawlFuncCount__'] =016 attrs['__CrawlFunc__'] =[]17 for k, v inattrs.items():18 if 'crawl_' ink: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.random31 }32

33 defget_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 returnself.proxies43

44 defcrawl_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 inurls: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 inzip(ip_list, port_list):57 yield ip + ":" +port58 exceptException as e:59 print(e)60

61 defcrawl_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 inurls: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 inip_list]74 port_list = [i.strip() for i inport_list]75 for ip, port inzip(ip_list, port_list):76 yield ip + ":" +port77 exceptException as e:78 print(e)79

80 defcrawl_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 inzip(ip_list, port_list):92 yield ip + ":" +port93 exceptException 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.53 Author: OniOn4 Site: http://www.cnblogs.com/TM0831/5 Time: 2019/2/14 12:196 """

7 from ProxyPool.crawl importCrawler8 from ProxyPool.pool importRedisClient9

10

11 classGetProxy:12 def __init__(self):13 self.crawler =Crawler()14 self.redis =RedisClient()15

16 defget_proxy(self):17 """

18 运行爬虫爬取代理19 :return:20 """

21 print("[INFO]Crawl Start...")22 count =023 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 inproxies: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.53 Author: OniOn4 Site: http://www.cnblogs.com/TM0831/5 Time: 2019/2/14 14:246 """

7 importtime8 importrandom9 importrequests10 from fake_useragent importUserAgent11 from ProxyPool.crawl importCrawler12 from ProxyPool.pool importRedisClient13

14

15 classTestProxy:16 def __init__(self):17 self.crawler =Crawler()18 self.redis =RedisClient()19 ua = UserAgent() #使用随机UA

20 self.headers ={21 "UserAgent": ua.random22 }23

24 deftest(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 inproxy_list:34 self.request(proxy)35 print("[INFO]Test End...\n\n")36

37 defrequest(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://" +proxy49 }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.53 Author: OniOn4 Site: http://www.cnblogs.com/TM0831/5 Time: 2019/2/14 14:266 """

7 from ProxyPool.pool importRedisClient8 from ProxyPool.get importGetProxy9 from ProxyPool.test importTestProxy10

11

12 classMain:13 def __init__(self):14 self.gp =GetProxy()15 self.tp =TestProxy()16 self.db =RedisClient()17

18 defrun(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!

python爬虫免费代理池_【Python3爬虫】教你怎么利用免费代理搭建代理池相关推荐

  1. python爬虫技术源码_实战|手把手教你用Python爬虫(附详细源码)

    大家好,我是J哥,专注原创,致力于用浅显易懂的语言分享爬虫.数据分析及可视化等干货,希望人人都能学到新知识.最近J哥做了个爬虫小项目,感觉还挺适合新手入门的,于是迫不及待想分享给大家. 什么是爬虫? ...

  2. python分布式爬虫及数据存储_分布式爬虫

    爬虫学习使用指南 Auth: 王海飞 Data:2018-07-05 Email:779598160@qq.com github:https://github.com/coco369/knowledg ...

  3. python实用脚本 知乎_停课不停学|38 个免费 Python 项目合集,从小白到老司机!...

    1665 年,牛顿在剑桥三一学院就读期间,伦敦发生了著名的鼠疫.这场鼠疫几乎摧毁了整个伦敦城,无论是下层人民还是王室贵族都难逃魔掌,几乎五分之一的伦敦人都在这场鼠疫中丧生. 牛顿被迫回家进行自我隔离, ...

  4. python人体行为识别代码_人体行为识别(骨架提取),搭建openpose环境,VS2019(python3.7)+openpose...

    这几天开始接触人体行为识别,经过多方对比后,选择了现在最热的人体骨架提取开源库,openpose. 下面就不多说了,直接开始openpose在win10下的配置: 需求如下: 1. VS2019    ...

  5. 爬虫取中间文本_小小爬虫批量抓取微信推文里的图片

    哈喽,大家好,今天给大家分享一个特别特别小的爬虫案例! 爬取微信推文中的图片!!!! 有人说,这有啥用,,,,万一人家推文是放的是以图片的方式放的某个PPT的内容呢,你想把它弄下来,咋整,就是爬取啦. ...

  6. c++ 线程池_基础篇:高并发一瞥,线程和线程池的总结

    进程是执行程序的实体,拥有独属的进程空间(内存.磁盘等).而线程是进程的一个执行流程,一个进程可包含多个线程,共享该进程的所有资源:代码段,数据段(全局变量和静态变量),堆存储:但每个线程拥有自己的执 ...

  7. python工程师需要什么技能_成为爬虫工程师需要哪些技能

    1.前段时间快要毕业,而我又不想找自己的老本行Java开发了,所以面了很多Python爬虫岗位. 因为我在南京上学,所以我一开始只是在南京投了简历,我一共面试了十几家企业,其中只有一家没有给我发off ...

  8. python爬虫去哪儿网_大型爬虫案例:爬取去哪儿网

    世界那么大,我想去看看.相信每到暑假期间,就会有很多人都想去旅游.但是去哪里玩,没有攻略这又是个问题.这次作者给大家带来的是爬取去哪网自由行数据.先来讲解一下大概思路,我们去一个城市旅行必定有一个出发 ...

  9. python爬虫结果是字节_入门爬虫?一文搞定!

    为了感谢大家对"Python客栈"的关注与支持,我们每天会在留言中随机抽取三位粉丝发放6.6元小红包.快来参与吧! 文章分三个个部分 两个爬虫库requests和selenium如 ...

最新文章

  1. 【洛谷p1313】计算系数
  2. java/jsp/sql server项目 字符编码统一_JSP开发过程遇到的中文乱码问题及解决方案...
  3. php+我的第一个程序,2. 第一个 C 程序
  4. Matlab/Simulink电力系统——无穷大功率电源供电系统三相短路仿真
  5. java全局变量怎么定义_Java开发知识点:如何理解Java函数式编程?
  6. 通过netstat+rmsock查找AIX端口对应进程
  7. json添加元素 vue_详解通过JSON数据使用VUE.JS
  8. Android Sqlite 数据初始化
  9. 用Hystrix保护您的应用程序
  10. Redis的管道pipeline
  11. 终于有人对语音技术来了次彻头彻尾的批判!
  12. 计算可靠度编制matlab,工程结构可靠度计算的Matlab实现
  13. 恢复win7开机动画
  14. 【图片】批量获取几万张图片
  15. Cknife与一句话木马提权
  16. 【测试】软件测试之测试用例的设计方法
  17. TCSVT2021:一种结合全局和局部细粒度特征的行人再识别方法
  18. php实现爬虫抓取法定节假日放假和补班安排数据
  19. 【Set】01-set参数
  20. useEffect五个经典问题实践总结

热门文章

  1. SCI《科学引文索引》
  2. 灵动微通用MCU新品-MM32F0130
  3. Java源码程序设计-房屋出租管理系统设计与实现
  4. 量化交易中如何判断趋势
  5. 太敏感,8h删!!!
  6. quickLook-决策树家族
  7. HTML5音频和视频处理
  8. k8s(Kubernetes)中Pod,Deployment,ReplicaSet,Service之间关系分析
  9. 上决╇ф的精确打击问题【最大流】
  10. BOAT: Bilateral Local Attention Vision Transformer