进击的爬虫:用Python搭建匿名代理池

專 欄

苍冥,Python中文社区专栏作者,澳洲华裔,目前在墨尔本某国际咨询公司任职Splunk Developer,擅长网络安全及攻防,热爱Python及爬虫,同时在学习Machine Learning。

邮箱:e0t3rx@gmail.com

Github:github.com/eastrd

——

01 写在前面

常听到很多人抱怨自己的IP因爬虫次数太多而被网站屏蔽,不得不频繁使用各种代理IP,却又因为网上的公开代理大部分都是不能使用,而又要花钱花精力去申请VIP代理,几番波折又遭屏蔽。特此写一篇如何利用Python搭建代理池的文章,以降低时间及精力成本,实现自动化获取活跃代理IP的功能。

02 运作原理

一、 网站代理获取

1. 爬免费代理网站的IP列表测试是否可用及是否是高匿

2. 若都是,则放进数据库,否则丢弃。

3. 重复第2步

二、 保证失效的代理能被尽快从代理池中挑出

1. 从爬虫数据库获取IP

2. 测试IP的可用性和匿名性

3. 如果可用且匿名,则保留,否则丢弃。

4. 重复第1步

说明①:可建立一个爬虫程序守护程序(Daemon),有此方面需要的小伙伴可自行谷歌,在此不多做介绍。

说明②:可建立一个对外代理信息接口,无论你用NodeJS或者Flask/Django或者PHP来写都没关系,在此也不多做介绍。

03 实现

建议库: requests, BeautifulSoup, re, sqlite3。

其中,用requests库获取代理网站页面,用BeautifulSoup和re两库来进行代理信息获取,用sqlite3来对这些信息进行存取。

如果必要(如代理网站有反爬虫策略时),可用PhantomJS替代requests,或用相应库进行数据清理(如base64解码)。

下面简单展示一下各部分的代码:

首先是选择多个能爬取代理且不容易被屏蔽IP的网站,此处以proxy-list.org为例:

  1. BASE_URL = "https://proxy-list.org/english/index.php?p="
  2. #IP地址及端口的正则
  3. Re_Pattern_IP = re.compile("(.*):")
  4. Re_Pattern_PORT = re.compile(":(.*)")
  5. #网站有11页,所以循环11次获取所有代理IP及端口
  6. for startingURL_Param in range(1,11):
  7. HTML_ProxyPage = requests.get(BASE_URL+str(startingURL_Param)).content
  8. soup = bs(HTML_ProxyPage,"html.parser")
  9. for Raw_ProxyInfo in soup.find_all("ul",{"class":None}):
  10. #此网站有用Base64简单对代理进行了加密,所以这里对其解码
  11. ip_port = base64.b64decode(Raw_ProxyInfo.find("li",{"class":"proxy"}).text.replace("Proxy('","").replace("')",""))
  12. #接下来利用正则从网页数据中提取我们需要的信息
  13. IP = re.findall(Re_Pattern_IP, ip_port)[0]
  14. PORT = re.findall(Re_Pattern_PORT, ip_port)[0]
  15. TYPE = Raw_ProxyInfo.find("li",{"class":"https"}).text

接下来是一段简易代理池框架类的代码,提供代理数据库的添加、删除、可连接性检测、匿名性检测:

  1. class ProxyPool:
  2. #初始化爬虫池数据库
  3. def __init__(self,ProxyPoolDB):
  4. self.ProxyPoolDB = ProxyPoolDB
  5. self.conn = sqlite3.connect(self.ProxyPoolDB, isolation_level=None)
  6. self.cursor = self.conn.cursor()
  7. self.TB_ProxyPool = "TB_ProxyPool"
  8. self.cursor.execute("CREATE TABLE IF NOT EXISTS "+self.TB_ProxyPool+"(ip TEXT UNIQUE, port INTEGER, protocol TEXT)")
  9. #添加代理IP进代理池的接口
  10. def addProxy(self, IP, PORT, PROTOCOL):
  11. self.cursor.execute("INSERT OR IGNORE INTO " + self.TB_ProxyPool+"(ip, port, protocol) VALUES (?,?,?)", [IP,PORT,PROTOCOL])
  12. #检查代理的匿名性及可连接性
  13. def testConnection(self, IP, PORT, PROTOCOL):
  14. proxies = { PROTOCOL: IP+":"+PORT }
  15. try:
  16. OrigionalIP = requests.get("http://icanhazip.com", timeout=REQ_TIMEOUT).content
  17. MaskedIP = requests.get("http://icanhazip.com", timeout=REQ_TIMEOUT, proxies=proxies).content
  18. if OrigionalIP != MaskedIP:
  19. return True
  20. else:
  21. return False
  22. except:
  23. return False
  24. #删除代理IP对应的数据库记录
  25. def delRecord(self, IP):
  26. self.cursor.execute("DELETE FROM "+self.TB_ProxyPool+" WHERE ip=?",(IP,))

下面是对代理池进行去“失效IP”的代码:

  1. #循环代理池,逐行测试IP地址端口协议是否可用
  2. def cleanNonWorking(self):
  3. for info in self.cursor.execute("SELECT * FROM "+self.TB_ProxyPool).fetchall():
  4. IP = info[0]
  5. PORT = str(info[1])
  6. PROTOCOL = info[2].lower()
  7. isAnonymous = self.testConnection(IP,PORT,PROTOCOL)
  8. if isAnonymous == False:
  9. #这条代理的可用性失效了,从数据库里删除
  10. self.delRecord(IP)
  11. #通过检测icanhazip.com回显来检测可用性及匿名性
  12. def testConnection(self, IP, PORT, PROTOCOL):
  13. proxies = { PROTOCOL: IP+":"+PORT }
  14. try:
  15. OrigionalIP = requests.get("http://icanhazip.com", timeout=REQ_TIMEOUT).content
  16. MaskedIP = requests.get("http://icanhazip.com", timeout=REQ_TIMEOUT, proxies=proxies).content
  17. if OrigionalIP != MaskedIP:
  18. return True
  19. else:
  20. return False
  21. except:
  22. return False

04 反思

这个项目是我年初时用Python练手写的,以现在的程度再来回顾,逻辑不够严谨,各类功能太过耦合,不少段落需要重写,因为代码是在校园网内所跑,所以还需要考虑到网络连接的稳定性,这就造成部分代码之间的混乱关系。

通过icanhazip.com来检测代理匿名性的方法或许有效,但却忽略了X-Forwarded-For的HTTP头,所以有很大风险,必须改进。

验证代理池内代理的有效性,需要多线程,目前的方案效率太低。

05 完整代码

放在此文章中的是代理池的核心代码,旨在提供各位读者能够自己实现的思路及参考。完整代码可在作者的Github主页中找到,Ubuntu 16.04及Kali下用Python 2.7测试可运行。

原文发布于微信公众号 - Python中文社区(python-china)

原文发表时间:2016-11-28

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏帘卷西风的专栏

从零开始编写网络游戏--基础篇(1)

       最近2周比较忙,没有抽出时间来写Blog,不过在这段时间里面把整个思路理了一遍,梳理了一下大纲,以后会多抽时间来写Blog。

12210
来自专栏Java帮帮-微信公众号-技术文章全总结

solr使用教程【面试+工作】

solr使用教程一【面试+工作】 Solr调研总结 开发类型全文检索相关开发 Solr版本4.2文件内容本文介绍solr的功能使用及相关注意事项;主要包括以下内...

5.9K60
来自专栏皮皮之路

【MySQL】通过Binary Log简单实现数据回滚(一)

457110
来自专栏极客猴

Django 学习笔记之模型(上)

上片文章讲解模板。你本文将讲解 “MTV” 中 M 层次,即模型层(数据存取层)。模型这内容比较多,我将其拆分为 3 个部分来讲解。同时,文章也配套了例子,你可...

8930
来自专栏张善友的专栏

Ibatis in action 电子书

电子书 ? 是ibatis 项目组写的ibatis开发的权威书籍.现在只有电子版,目前使用Java作为描述的平台,这个书对于.NET和Ruby一样适用.    ...

22270
来自专栏pangguoming

PowerDesigner使用教程|使用方法

PowerDesigner安装方法: http://dev.firnow.com/course/3_program/java/javajs/20090908/...

54960
来自专栏互联网高可用架构

初识分库分表框架DBSPLIT

19940
来自专栏pangguoming

分布式系统唯一ID生成方案汇总

系统唯一ID是我们在设计一个系统的时候常常会遇见的问题,也常常为这个问题而纠结。生成ID的方法有很多,适应不同的场景、需求以及性能要求。所以有些比较复杂的系统会...

65260
来自专栏互联网技术栈

Elasticsearch之元数据(meta-fields)介绍

在Elasticsearch下,一个文档除了有数据之外,它还包含了元数据(Metadata)。每创建一条数据时,都会对元数据进行写入等操作,当然有些元数据是在创...

21960
来自专栏程序员的SOD蜜

EF+MySQL乐观锁控制电商并发下单扣减库存,在高并发下的问题

下订单减库存的方式 现在,连农村的大姐都会用手机上淘宝购物了,相信电商对大家已经非常熟悉了,如果熟悉电商开发的同学,就知道在买家下单购买商品的时候,是需要扣减库...

80180

扫码关注云+社区

领取腾讯云代金券