环境: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服务

代理池工作流程

image-20191024195902163

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
    • 用于默认代理的分数,配置日志格式,文件,启动的爬虫,检验的间隔时间等.

代理池的项目结构

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
--IPProxyPoo1
	--core
		--db
			--__init__.py
			--mongo_pool.py
		--proxy_validate
			--__init__.py
			--httpbin_validator.py
		--proxy_spider
			--__init__.py
			--base_spider.py
			--proxy_kpiders.py
			--run_spiders.py
		--proxy_test.py
		--proxy_api.py
	--domain.py
	--utils
        --__init__.py
        --http.py
        --log.py
	--main.py
	--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方法