环境:python3.6
MongoDB
flask
requests等第三方库
完整代码见: https://github.com/Lucareful/IPProxyPool
代理池概述
什么是代理池
- 代理池就是有代理IP组成的池子,它可以提供多个稳定可用的代理IP
为什么要实现代理池
我们在做爬虫的时候,最常见的一种反爬虫手段就是:IP反爬;也就是当同一个IP访问这个网站的次数过多,频率过高,就会限制这个IP的访问。就是需要经常换IP;
- 使用IP代理池是其中一个比较常用的方案
- 免费代理都是非常不稳定的,有10%是可用就很不错了
- 一些收费代理稳定性也不好
目的:从一堆不稳定的代理IP中,抽取高可用代理IP,给爬虫使用
代理池开发环境
python3开发语言
requests:发送请求,获取页面数据
lxml:使用XPATH从页面提取我们想要的书籍
pymonge:把提取到代理IP存储到MongoDB数据库中和MongoDB数据库中读取代理IP,给爬虫使用
Flask:用于提供WEB服务
代理池工作流程
1.代理池工作渡程描述:
- 代理IP采集模块->采集代理IP->检测代理IP->如果不可用用,直接过滤掉,如果可用,指定默认分数->存入数据库中
- 代理IP检测模块->从数据库中获取所有代理IP->检测代理IP->如果代理IP不可用用,就把分数-1,如果分数为0从数据库中删除,否则更新数据库,如果代理IP可用,恢复为默认分值,更新数据库
- 代理API模块->从数据库中高可用的代理IP给爬虫使用;
代理池的模块及其作用
五大核心模块
- 爬虫模块
- 从代理IP网站上采集代理IP
- 进行校验(获取代理响应速度,协议类型,匿名类型)
- 把可用代理IP存储到数据库中
- 代理IP的校验模块:获取指定代理的响应速度,支持的协议以及匿名程度
- 原因:网站上所标注的响应速度,协议类型和匿名类型是不准确的
- 这里使用httpbin.org进行检测
- 数据库模块:实现对代理IP的增删改查操作
- 这里使用MongoDB来存储代理IP
- 检测模块:定时的对代理池中代理进行检测,保证代理池中代理的可用性.
- 从数据库读取所有的代理IP
- 对代理IP进行逐一检测,可用开启多个协程,以提高检测速度
- 如果该代理不可用,就让这个代理分数-1,当代理的分数到0了,就删除该代理;如果检测到代理可用就恢复为满分.
- 代理IP服务接口:提供高可用的代理IP给爬虫使用
- 根据协议类型和域名获取随机一个高质量代理IP
- 根据协议类型和域名获取多个高质量代理IP
- 根据代理IP不可用域名,告诉代理池这个代理IP在该域名下不可用,下次获取这个域名的代理IP时候,就不会再获取这个代理IP了,从而保证代理IP高可用性.
代理池的其它模块
- 数据模型:domain.py
- 代理IP的数据模型,用于封装代理IP相关信息,比如ip,端口号,响应速度,协议类型,匿名类型,分数等
- 程序启动入口:main.py·代理池提供一个统一的启动入口
- 工具模块:、
- 日志模块:用于记录日志信息
- http模块:用于获取随机User-Agent的请求头
- 配置文件:settings.py
- 用于默认代理的分数,配置日志格式,文件,启动的爬虫,检验的间隔时间等.
代理池的项目结构
|
|
定义代理IP的数据模型类
- 定义Proxy类,继承object
- 实现_init_方法,负责初始化,包含如下字段:
- ip:代理的IP地址
- port:代理IP的端口号
- protocol:代理IP支持的协议类型,http是0,https是1,https和http都支持是2
- nick_type:代理IP的匿名程度,高匿:0,匿名:1,透明:2
- speed:代理IP的响应速度,单位s
- area:代理IP所在地区
- score:代理IP的评分,用于衡量代理的可用性;默认分值可以通过配置文件进行配置.在进行代理可用性检查的时候,每遇到一次请求失败就减1份,减到0的时候从池中删除.如果检查代理可用,就恢复默认分值
- disable_domains:不可用域名列表,有些代理IP在某些域名下不可用,但是在其他域名下可用
- 在配置文件:settings.py中定义MAX_SCORE=50,表示代理IP的默认最高分数
- 提供_str方法,返回数据字符串
代理池的工具模块
日志模块
http模块
日志模块
能够方便的对程序进行调试
能够方便记录程序的运行状态
能够方便记录错误信息
日志的实现
- 目标:实现日志模块,用于记录日志
- 把日志相关配置信息放到配置文件中
- 修改日志代码,使用配置文件中的配置信息
Http模块
- 我在从代理IP网站上抓取代理IP和检验代理IP时候,为了不容易不服务器识别为是一个爬虫,我们最好提供随机的User-Agent请求头.
- 目标:获取随机User-Agent的请求头 步骤: 1.准备User-Agent的列表 2.实现一个方法,获取随机User-Agent的请求头
代理池的检验模块
目标:检查代理IP速度,匿名程度以及支持的协议类型.
步骤:
检查代理IP速度和匿名程度;
代理IP速度:就是从发送请求到获取响应的时间间隔
匿名程度检查:
对http://httpbin.org/get 或https://httpbin.org/get 发送请求
如果响应的origin中有 ***,***分割的两个IP就是透明代理IP
如果响应的headers 中包含Proxy-Connection 说明是匿名代理IP
否则就是高匿代理IP。
检查代理IP协议类型
- 如果http://httpbin.org/get 发送请求可以成功,说明支持http协议
如果https://httpbin.org/get 发送请求可以成功,说明支持https协议
代理池的数据库模块
- 作用:用于对proxies集合进行数据库的相关操作
- 目标:实现对数据库增删改查相关操作、
- 步骤:
1.在
init
中,建立数据连接,获取要操作的集合,在del
方法中关闭数据库连接 2.提供基础的增删改查功能- 实现插入功能
- 实现修改该功能
- 实现删除代理:根据代理的IP删除代理
- 查询所有代理IP的功能
- 3.提供代理API模块使用的功能
- 实现查询功能:根据条件进行查询,可以指定查询数量,先分数降序,速度升序排,保证优质的代理IP在上面.
- 实现根据协议类型和要访问网站的域名,获取代理IP列表
- 实现根据协议类型和要访问完整的域名,随机获取一个代理IP
- 实现把指定域名添加到指定IP的disable_domain列表中.
实现代理池的爬虫模块
爬虫模块的需求
- 需求:抓取各个代理IP网站上的免费代理IP进行检测,如果可用存储到数据库中
- 需要抓取代理IP的页面如下:
- 西刺代理:https://www.xicidaili.com/nn/1
- ip3366代理:http://www.ip3366.net/free/?stype=1&page=1
- 快代理:https://www.kuaidaili.com/free/inha/1/
- oproxylistplus代理:https://list.proxylistplus.com/Fresh-HTTP-Proxy-List-1
- 66ip代理:http://www.66ip.cn/1.html
爬虫模块的设计
- 通用爬虫:通过指定URL列表,分组XPATH和组内XPATH,来提取不同网站的代理IP
- 原因代理IP网站的页面结构几乎都是Table,页面结构类似
- 具体爬虫:用于抓取具体代理IP网站
- 通过继承通用爬虫实现具体网站的抓取,一般只需要指定爬取的URL列表,分组的XPATH和组内XPATH就可以了.
- 如果该网站有特殊反爬手段,可以通过重写某些方法实现反爬
- 爬虫运行模块:启动爬虫,抓取代理IP进行检测,如果可用,就存储到数据库中;
- 通过配置文件来控制启动哪些爬虫,增加扩展性;如果将来我们遇到返回
json
格式的代理网站,单独写一个爬虫配置下就好了.
- 通过配置文件来控制启动哪些爬虫,增加扩展性;如果将来我们遇到返回
实现通用爬虫
目标:实现可以指定不同URL列表,分组的XPATH和详情的XPATH,从不同页面上提取代理的IP端口号和区域的通用爬虫;
步骤: 1.在
base_spider.py
文件中,定义一个BaseSpider
类,继承object 2.提供三个类成员变量:urls:代理IP网址的URL的列表
group_xpath:分组XPATH,获取包含代理IP信息标签列表的XPATH
detail_xpath:组内XPATH,获取代理IP详情的信息XPATH,格式为:{“ip’:‘xx’,‘port’:‘xx’,‘area’:‘xx’}
3.提供初始方法,传入爬虫URL列表,分组XPATH,详情(组内)XPATH 4.对外提供一个获取代理IP的方法
- 遍历URL列表,获取URL
- 根据发送请求,获取页面数据
- 解析页面,提取数据,封装为Proxy对象
- 返回Proxy对象列表
实现具体爬虫
目标:通过继承通用爬虫,实现多个具体爬虫,分别从各个免费代理IP网站上抓取代理IP
1.实现西刺代理爬虫:http://www.xicidaili.com/nn/1
- 定义一个类,继承通用爬虫类(
BasicSpider
) - 提供urls,group_xpath 和detail_xpath
- 定义一个类,继承通用爬虫类(
2.实现ip3366代理爬虫:http://www.ip3366.net/free/?stype=1&page=1
- 定义一个类,继承通用爬虫类(BasicSpider)
- 提供urls,group_xpath 和detail_xpath
3.实现快代理爬虫:https://www.kuaidaili.com/free/inha/1/
- 定义一个类,继承通用爬虫类(BasicSpider)
- 提供urls,group_xpath和detail_xpath
4.实现 proxylistplus代理爬虫:https://list.proxylistplus.com/Fresh-HTTP-Proxy-List-1
- 定义一个类,继承通用爬虫类(BasicSpider)
- 提供urls,group_xpath 和detail_xpath
5.实现66ip爬虫:http://www.66ip.cn/1.html
- 定义一个类,继承通用爬虫类(BasicSpider)
- 提供urls,group_xpath和detail_xpath
- 由于66ip网页进行js+cookie反爬,需要重写父类的get_page_from_url 方法
PS:实现66ip爬虫:http://www.66ip.cn/1.html核心:
通过加密的js,生成需要cookie信息 1.从响应页面中,提取:
1.执行生成真正js语句
2.生成真正js的函数.
2.网页中,是通过
eval
执行真正js,加载页面;而我们要获取真正的js;我们就需要把eval
语句,替换为return,把真正js返回.3.使用
js2py
,获取执行js的环境,使用js执行环境加载这个函数4.使用这个执行环境,执行调用调用,生成真正js,赋值给一个变量
5.从真正的js代码中,提取我们需要cookie信息.
实现爬虫的运行模块
- 目标:根据配置文件信息,加载爬虫,抓取代理IP,进行校验,如果可用,写入到数据库中
- 思路:
- 在
run_spider.py
中,创建RunSpider
类
- 在
- 提供一个运行爬虫的run方法,作为运行爬虫的入口,实现核心的处理逻辑
- 根据配置文件信息,获取爬虫对象列表.
- 遍历爬虫对象列表,获取爬虫对象,遍历爬虫对象的get_proxies方法,获取代理IP
- 检测代理IP(代理IP检测模块)
- 如果可用,写入数据库(数据库模块)
- 处理异常,防止一个爬虫内部出错了,影响其他的爬虫.
- 使用异步来执行每一个爬虫任务,以提高抓取代理IP效率
- 在init 方法中创建协程池对象
- 把处理一个代理爬虫的代码抽到一个方法
- 使用异步执行这个方法
- 调用协程的join方法,让当前线程等待队列任务的完成.
- 使用
schedule
模块,实现每隔一定的时间,执行一次爬取任务- 定义一个start的类方法
- 创建当前类的对象,调用run方法
- 使用schedule模块,每隔一定的时间,执行当前对象的run方法
实现代理池的检测模块
目的:检查代理IP可用性,保证代理池中代理IP基本可用
思路
1.在
proxy_test.py
中,创建Proxy Tester
类2.提供一个run 方法,用于处理检测代理命核心逻辑
从数据库中获取所有代理IP
遍历代理IP列表
检查代理可用性
如果代理不可用,让代理分数-1,如果代理分数等于0就从数据库中删除该代理,否则更新该代理IP
如果代理可用,就恢复该代理的分数,更新到数据库中
3.为了提高检查的速度,使用异步来执行检测任务
在
init
方法中,创建队列和协程池- 把要检测的代理IP,放到队列中
- i.把检查一个代理可用性的代码,抽取到一个方法中;从队列中获取代理IP,进行检查;检查完毕,调度队列的task_done方法
- ii.通过异步回调,使用死循环不断执行这个方法,
- iv.开启多个一个异步任务,来处理代理IP的检测;可以通过配置文件指定异步数量
- 调用队列的join方法,让当前线程等待队列任务完成
4.使用schedule模块,每隔一定的时间,执行一次检测任务
- 定义类方法start,用于启动检测模块
- 在start方法中
- i.创建本类对象
- i.调用run方法
- i.每间隔一定时间,执行一下,run方法
实现代理池的API模块
目标:
- 为爬虫提供高可用代理IP的服务接口
步骤:
实现根据协议类型和域名,提供随机的获取高可用代理IP的服务
实现根据协议类型和域名,提供获取多个高可用代理IP的服务
实现给指定的IP上追加不可用域名的服务
实现
- 在
proxy_api.py
中,创建ProxyApi
类
- 在
实现初始方法s
初始一个Flask的Web服务
实现根据协议类型和域名,提供随机的获取高可用代理IP的服务
可用通过protocol和domain参数对IP进行过滤
protocol:当前请求的协议类型
domain:当前请求域名
实现根据协议类型和域名,提供获取多个高可用代理IP的服务·
- 可用通过protocol和domain参数对IP进行过滤
实现给指定的IP上追加不可用域名的服务
- 如果在获取IP的时候,有指定域名参数,将不在获取该IP从而进一步提高代理IP的可用性.
实现run方法,用于启动Flask的WEB服务
实现start的类方法,用于通过类名,启动服务
实现代理池的程序入口
- 目标:把
启动爬虫
,启动检测代理IP
,启动WEB服务
统一到一起 - 思路:
- 开启三个进程,分别用于启动爬虫,检测代理IP,WEB服务
- 步骤:
- 定义一个run方法用于启动动代理池
- 定义一个列表,用于存储要启动的进程
- 创建
启动爬虫
的进程,添加到列表中 - 创建
启动检测
的进程,添加到列表中 - 创建
启动提供API
服务的进程,添加到列表中 - 遍历进程列表,启动所有进程
- 遍历进程列表,让主进程等待子进程的完成
- 在
if__name__=='__main__':
中调用run方法