github项目地址:
https://github.com/wenwenc9/proxypool.git
网上有很多的代理池教学与文章,但是基本上都是通过爬取众多免费代理网站进行处理,或者讲的并不清楚
那么付费代理了?如果使用过付费代理的人,应该知道付费代理,都有个ip存活时间
本文以芝麻代理作为搭建 http://www.zhimahttp.com/index/
复用性
,每次爬虫采集都会用到ip ,不可能使用一次就用一次四分钱吧即时性
,ip一但被封禁对ip进行标记,不再采用此ip多可用
,多个爬虫项目分别A网站,B网站,C网站,C网站可以用同时使用这一个ip针对性
,A网站使用此IP的使用被网站封禁,那么这个IP应当进行标记 A 不再使用,但是B,C任然可以使用使用次数
,针对此条IP,被A,B,C三个采集调用共计多少次,这能助于我们对于程序参数调整,对代理IP的消耗情况进行统计、提供方法让爬虫程序通过简单的配合后能统计出代理IP的数据产出率、代理可用率进行统计,即可视化开发但是付费代理也有缺点:
项目结构
server
– config.py 配置文件。
– ip 客户端请求后获取的客户端IP,文本保存。
– main.py
– Flask主程序,提供两个接口,一个是接收客户端请求,然后将IP保存,另外一个是获取当前保存的IP。
client
– crontab定时任务命令示例。
– pppoe.sh 拨号脚本,主要是实现重新拨号的几个命令。
– request.sh
– 请求服务器的脚本,主要是实现拨号后请求服务器的操作。
– reqeust.conf 配置文件。
核心模块组成:调度器、添加器、校验器、下载器,redis组件,flask API接口
调度器
全局管控,相当于CPU,管控添加器、校验器
下载器
从IP代理服务商,提取代理IP,http/https类型
篮子
存放校验的ip
添加器
调用下载器,添加IP到篮子
篮子里面假如达到100个IP,启动校验器
校验器
校验篮子里面IP是否过期,如果没有过期,调用redis组件,进行存储
循环校验校验redis数据库中IP是否过期,如果有,调用redis组件,进行删除
redis组件
封装了增删改查功能
flask API接口
提供网页说明文档,让人快速了解接口与使用方法
负责功能 :
redis特性即为什么选择redis:
list数据类型提供双向链路,栈,队列功能
list可以实现队列功能,先入后出,因此ip经过校验,存放到list,那么后面校验的为最新的校验成功的ip
因此调用pop()
方法弹出尾部最新的可用代理ip,就可以提高代理可用性
同样的配合get()
方法头部获取,经过校验存入的旧的代理ip从头部取出来
put()
,将新校验的代理ip,存入list尾部
创建 Redis_Client(object)
类
将来这个类,是能够封装调用的,连接redis,host等参数应该不能固定
因此需要配置到配置文件,新建 settings.py
集合分别存储http,https类型的ip
# ---------------------- redis 配置 -----------------------
HOST = 'localhost'
PORT = 6380
PASSWORD = ''
PROXY_NAME = 'proxies' # 数据库名称
HTTP_PROXY_NAME = 'http_proxies' # HTPP IP存储
HTTPS_PROXY_NAME = 'https_proxies' # HTTPS IP存储
写完毕后,db.py
从 settings.py
调用参数就可以了,新建 db.py
import redis
from settings import HOST, PASSWORD, PORT, PROXY_NAME, HTTP_PROXY_NAME, HTTPS_PROXY_NAME
class Reids_Client(object):
def __init__(self, host=HOST, port=PORT, password=PASSWORD):
if password:
self.__conn = redis.Redis(host=host, port=port, password=password)
else:
self.__conn = redis.Redis(host=host, port=port)
在Reids_Client
类中实现 put()方法,向尾部添加一个代理
def put(self, proxy, types):
"""
向代理池尾部添加一个代理
:param proxy: 代理ip
:param types: 代理类型
:return:
"""
# self.__conn.rpush(PROXY_NAME, proxy)
if types == 'http':
self.__conn.rpush(HTTP_PROXY_NAME, proxy)
if types == 'https':
self.__conn.rpush(HTTPS_PROXY_NAME, proxy)
在Reids_Client
类实现pop()
方法,获取一个代理
def pop(self, types):
"""
获取一个代理
redisd的存储所有数据都是bytes类型-
:param types: 代理类型
:return: 可用的最新代理
"""
# return self.__conn.rpop(PROXY_NAME).decode('utf-8')
if types == 'http':
return self.__conn.rpop(HTTP_PROXY_NAME).decode('utf-8')
if types == 'https':
return self.__conn.rpop(HTTPS_PROXY_NAME).decode('utf-8')
在Reids_Client
类实现get()
方法,get()
应该获取多个代理ip校验
def get(self, count=1, types=None):
"""
获取count个代理,同时将这些数据删除
:param count: 默认数量为1
:param types: 代理类型
:return:
"""
if types == 'http':
proxies = self.__conn.lrange(HTTP_PROXY_NAME, 0, count - 1)
self.__conn.ltrim(HTTP_PROXY_NAME, count, -1)
return proxies
if types == 'https':
proxies = self.__conn.lrange(HTTPS_PROXY_NAME, 0, count - 1)
self.__conn.ltrim(HTTPS_PROXY_NAME, count, -1)
return proxies
在Reids_Client
类实现queue_len()
统计代理池的长度
def queue_len(self, types=None):
"""
针对添加器,什么是开始添加,什么时候结束添加,应该查看代理池有无数据,那么查看代理池的长度
:param types: 代理类型
:return:
"""
# return self.__conn.llen(PROXY_NAME)
if types == None:
http_parameters = (self.__conn.llen(HTTP_PROXY_NAME), HTTP_PROXY_NAME,'http')
https_parameters = (self.__conn.llen(HTTPS_PROXY_NAME), HTTPS_PROXY_NAME,'https')
return http_parameters, https_parameters
elif types == 'http':
return self.__conn.llen(HTTP_PROXY_NAME)
elif types == 'https':
return self.__conn.llen(HTTPS_PROXY_NAME)
在Reids_Client
类 实现flush()
方法
flushdb()
清空数据库 def flush(self):
self.__conn.flushdb() # 清空代理池
# -*- coding: utf-8 -*-
# @Time : 2020-12-28 10:17
# @Author : XuGuangJun
# @FileName: db.py
# @Software: PyCharm
import redis
from settings import HOST, PASSWORD, PORT, PROXY_NAME, HTTP_PROXY_NAME, HTTPS_PROXY_NAME
class Reids_Client(object):
def __init__(self, host=HOST, port=PORT, password=PASSWORD):
if password:
self.__conn = redis.Redis(host=host, port=port, password=password)
else:
self.__conn = redis.Redis(host=host, port=port)
def put(self, proxy, types):
"""
向代理池尾部添加一个代理
:param proxy: 代理ip
:param types: 代理类型
:return:
"""
# self.__conn.rpush(PROXY_NAME, proxy)
if types == 'http':
self.__conn.rpush(HTTP_PROXY_NAME, proxy)
if types == 'https':
self.__conn.rpush(HTTPS_PROXY_NAME, proxy)
def lput(self, proxy, types):
'''
向代理池头部部添加一个使用过的代理
:param proxy:
:return:
'''
# self.__conn.lpush(PROXY_NAME, proxy)
if types == 'http':
self.__conn.lpush(HTTP_PROXY_NAME, proxy)
if types == 'https':
self.__conn.lpush(HTTPS_PROXY_NAME, proxy)
def pop(self, types):
"""
获取一个代理
redisd的存储所有数据都是bytes类型-
:param types: 代理类型
:return: 可用的最新代理
"""
# return self.__conn.rpop(PROXY_NAME).decode('utf-8')
if types == 'http':
return self.__conn.rpop(HTTP_PROXY_NAME).decode('utf-8')
if types == 'https':
return self.__conn.rpop(HTTPS_PROXY_NAME).decode('utf-8')
def get(self, count=1, types=None):
"""
获取count个代理,同时将这些数据删除
:param count: 默认数量为1
:param types: 代理类型
:return:
"""
if types == 'http':
proxies = self.__conn.lrange(HTTP_PROXY_NAME, 0, count - 1)
self.__conn.ltrim(HTTP_PROXY_NAME, count, -1)
return proxies
if types == 'https':
proxies = self.__conn.lrange(HTTPS_PROXY_NAME, 0, count - 1)
self.__conn.ltrim(HTTPS_PROXY_NAME, count, -1)
return proxies
def queue_len(self, types=None):
"""
针对添加器,什么是开始添加,什么时候结束添加,应该查看代理池有无数据,那么查看代理池的长度
:param types: 代理类型
:return:
"""
# return self.__conn.llen(PROXY_NAME)
if types == None:
http_parameters = (self.__conn.llen(HTTP_PROXY_NAME), HTTP_PROXY_NAME,'http')
https_parameters = (self.__conn.llen(HTTPS_PROXY_NAME), HTTPS_PROXY_NAME,'https')
return http_parameters, https_parameters
elif types == 'http':
return self.__conn.llen(HTTP_PROXY_NAME)
elif types == 'https':
return self.__conn.llen(HTTPS_PROXY_NAME)
def flush(self):
self.__conn.flushdb() # 清空代理池
新建 getter.py
新建 IpProxyMeta(type)
继承 type,即python的所有类的祖宗这样可以达到自定义元类方法,如果谁继承了这个自定义类,那么就调用__Protocol_Func__
,这个属性,就能打印出此函数里面的所有函数名称
新建 IpProxyGetter(metaclass=IpProxyMeta)
类,并在其内定义三个函数,分别从付费代理接口
获取http,和https接口获取代理IP,以及调用http还是https函数的函数
返回json,包含了 IP、过期时间、等字段
经过protocol_http处理后返回的ip为json(原本API接口返回的字段很多,可以自己查看,现在只保留了想要的,和添加自己想要的)
ip,过期时间,类型,使用次数
{"ip": '', "expire_time":'', "types": "https","use_num": '0'}
{"ip": '', "expire_time":'', "types": "http","use_num": '0'}
来到 settings.py
设置HTTP_URL
与 HTTPS_URL
地址
HTTP_URL = 'http://webapi.http.zhimacangku.com/getip?num=20&type=2&pro=0&city=0&yys=0&port=1&time=1&ts=1&ys=0&cs=0&lb=1&sb=0&pb=45&mr=1®ions='
HTTPS_URL = 'http://webapi.http.zhimacangku.com/getip?num=20&type=2&pro=0&city=0&yys=0&port=11&time=1&ts=1&ys=0&cs=0&lb=1&sb=0&pb=45&mr=1®ions='
这个芝麻代理API接口来源,http和https分别执行下面参数两次
一定要勾选过期时间,因为要校验IP的存活时间,达到复用性
点击后鼠标向下滑动下,这就是API接口
复制完毕,打开,如果需要加入白名单,来到主页加入白名单即可
getter.py
需要导入HTTP_URL
和 HTTPS_URL
两个参数
关于这个元类执行效果:
可能会说,python当中不是自带有__dir__能够打印出这个类中所有方法不是吗,当然没错
但是过于复杂,包含了所有的执行方法,并不能过滤掉我们不想要的
那么使用继承元类效果如下
# -*- coding: utf-8 -*-
# @Time : 2020-12-28 10:24
# @Author : XuGuangJun
# @FileName: getter.py
# @Software: PyCharm
import requests, re
from settings import HTTP_URL, HTTPS_URL
# from proxypool.settings import HTTP_URL, HTTPS_URL
import time
class IpProxyMeta(type):
"""
继承python所有类的祖宗,即自定义元类
"""
def __new__(cls, name, bases, attrs):
# protocol 协议 指定为http还是https、scoks5
attrs['__Protocol_Func__'] = []
for k, v in attrs.items():
if 'protocol_' in k:
attrs['__Protocol_Func__'].append(k)
return type.__new__(cls, name, bases, attrs)
class IpProxyGetter(metaclass=IpProxyMeta):
"""
从IP代理服务商,提取代理IP
http
https
socks5
隧道代理
"""
def get_raw_proxies(self, callfunc):
""" # TODO 使用异步协程来处理, 这里并未实现真正意义上阈值处理
通过给指定名字来调用对应方法,获取某种协议代理如callfunc = protocol_http
获得的为http协议的IP
:param callfunc:
:return:
"""
proxies = []
for proxy in eval(f'self.{callfunc}()'):
proxies.append(proxy)
return proxies # 这里有点不好,必须等所有的proxy完毕,才返回,也可以通过生成器 yield 改装一下 断点
def protocol_http(self):
"""
获取 http 类型代理
"""
time.sleep(2)
if HTTP_URL != None:
proxies = []
response = requests.get(
url=HTTP_URL
)
json = response.json()['data']
# 获取时间
for i in json:
ip = i['ip'] + ":" + str(i['port'])
res = {"ip": "{}".format(ip), "expire_time": "{}".format(i['expire_time']), "types": "http",
"use_num": '0'}
proxies.append(res)
# yield res
return proxies
def protocol_https(self):
"""
获取 https 类型代理
"""
time.sleep(2)
if HTTPS_URL != None:
proxies = []
response = requests.get(
url=HTTPS_URL
)
json = response.json()['data']
# print(json)
# 获取时间
for i in json:
ip = i['ip'] + ":" + str(i['port'])
res = {"ip": "{}".format(ip), "expire_time": "{}".format(i['expire_time']), "types": "https",
"use_num": '0'}
# proxies.append(res)
yield res
# return proxies
return None
if __name__ == '__main__':
f = IpProxyGetter()
print(f.__dir__())
# print(f.__Protocol_Func__)
# print(IpProxyGetter().protocol_http())
调度器职责就是调度校验器、添加器
调取器就是,包含了控制开关,添加器,校验器功能,那么就需要逐一开发其中的功能
负责功能:
创建类 VaildityTester(object)
def __init__(self):
# 设为私有,不允许外部调用,存放待校验ip
self.__raw_proxies = []
def set_raw_proxies(self, proxiies):
"""
set_raw_proxies 集原料代理
放入未校验ip
:param proxies: 未校验ip 从getter 中传入待校验ip
:return:
"""
self.__raw_proxies = proxiies
# 数据库连接-->创建的方式会影响程序性能--》用的时候创建
self.__conn = Reids_Client() # redis数据库对象
get_ProxyPararmeter
方法判断是从reids中取出的ip(bytes类型),还是从代理商接口获得的ip(str类型)
,bytes需要进行转换。校验IP是否过期功能
,p过期不能刚好到那个点,应当提前,所以设置 IP_LIVE
参数,应当提前删除快过期IP def get_ProxyPararmeter(self, ProxyParameter, ip_live=IP_LIVE):
"""
获取代理校验参数
ProxyParameter:{'ip': ip, 'expire_time': i['expire_time'], 'types': 'https'}
"""
# proxy是从redis取出,是bytes类型,所以无法判断,进行转换
if isinstance(ProxyParameter, bytes):
# 转换为字符串
ProxyParameter = ProxyParameter.decode('utf-8')
"""
这里有2个逻辑,getter下载器yield过来的为dcit
进行校验的为str类型
"""
if type(ProxyParameter) == str:
ProxyParameter = json.loads(re.sub(r"'", '"', ProxyParameter))
now_time = int(time.time())
# 将其转换为时间戳
timeArray = time.strptime(ProxyParameter['expire_time'], "%Y-%m-%d %H:%M:%S")
Expiration_timeStamp = int(time.mktime(timeArray))
# 判断是否过期,并且存储
if (Expiration_timeStamp - ip_live) > now_time:
if ProxyParameter['types'] == 'http':
self.__conn.put(str(ProxyParameter), types='http') # redis当中存储
if ProxyParameter['types'] == 'https':
self.__conn.put(str(ProxyParameter), types='https') # redis当中存储
print('校验成功代理:', ProxyParameter)
else:
print('\033[1;31m已过期代理:{}\033[0m'.format(ProxyParameter))
创建开关 verify_check_switch
此开关由调度器把控,开与不开,即scheduler调度,接收到调用信号就开始调用
def verify_check_switch(self):
"""
校验器开关
"""
print('\033[1;30m{}\033[0m'.format('-' * 23 + ' VaildityTester 校验器启动(校验内存中IP/redis中的IP,并且存储) ' + '-' * 23))
for ProxyParameter in self.__raw_proxies:
self.get_ProxyPararmeter(ProxyParameter)
负责功能:
创建 PoolAdder 类
需要初始化条件
def __init__(self, threshold=UPPER_THRESHOLD):
# 创建存入redis数据库中最大代理池数量阈值 内置条件
self.__threshold = threshold # 60
# 创建开关实例
self.__tester = VaildityTester()
# 创建redis数据库对象实例
self.__conn = Reids_Client()
# 创建getter实例
self.__getter = IpProxyGetter()
在类中继续完成,是否达到阈值
def is_over_threshold(self, types):
"""
判断代理池中代理的数量是否达到最大值
:return True: 超过阈值
"""
if int(self.__conn.queue_len(types=types)) >= self.__threshold:
return True
return False
创建 add_to_pool
类,其中types
参数作为http与https区别,里面进行循环判断,时刻判断代理池是否高于阈值,其中__getter.__Protocol_Func__
,中调用了封装的元类,通过调用,分别存储到不同的篮子
其中 proxy_count == 0,出现此异常为,付费代理请求API接口过快导致,或者API接口需要添加白名单。
def add_to_pool(self, types):
"""
添加代理 添加到篮子
:return:
"""
print('\033[1;30m{}\033[0m'.format('-' * 28 + ' PoolAdder 添加器启动(代理商获得IP 存入内存 启动校验) ' + '-' * 28))
# 循环获取ip (获取材料)
while True:
if self.is_over_threshold(types=types): # 判断是否低于阈值
print(f'目前代理池的数量为限定{UPPER_THRESHOLD},不需要下载IP')
break
proxy_count = 0
for _ in self.__getter.__Protocol_Func__:
# 判断是那种类型
if types == 'http':
proxies = self.__getter.get_raw_proxies('protocol_http')
if len(proxies) > 0:
proxy_count += len(proxies)
# 使用校验器校验,是否过期
self.__tester.set_raw_proxies(proxies) # 放入材料
self.__tester.verify_check_switch() # 开启机器(开启校验方法)
if types == 'https':
proxies = self.__getter.get_raw_proxies('protocol_https')
if len(proxies) > 0:
proxy_count += len(proxies)
# 使用校验器校验,是否过期
self.__tester.set_raw_proxies(proxies) # 放入材料
self.__tester.verify_check_switch() # 开启机器(开启校验方法)
if proxy_count == 0: #
# 判出一个运行时间的异常
raise RuntimeError('获取代理时间过快,可忽略此异常!')
创建 scheduler.py
调度器负责
VaildityTester()
PoolAdder()
创建 Scheduler(object)
,那么还需要定义两个方法,valid_proxy
控制校验器,check_pool_add
控制添加器
添加和校验应当是都在一直运行的
run()
方法,使用多进程完成控制校验器代码 VaildityTester()
:
关于 valid_proxy(cycle=CYCLE_VAILD_TIME)
,即调度器控制,控制调度器做什么?
IP_CEHCK_COUNT
参数可以指定CYCLE_VAILD_TIME
是针对while的一个循环时间,不断从代理池当中头部获取一片,进行定期检查完成控制添加器代码 PoolAdder()
创建check_pool_add
类,需要传入三个参数
lower_threshold
IP代理池最低数量时候,控制添加器开关(添加器控制下载器),获取一片新IP到篮子upper_threshold
IP代理池数量达到这个阈值的时候,关闭添加器开关cycle
循环添加器功能,看是否最低或最高情况因为有两种类型IP,http和https,因此需要线程添加http和https
class Scheduler(object):
"""
调度器
"""
# 1、循环校验过程 不断从带池中头部获取一片,做定期检查
@staticmethod
def valid_proxy(cycle=CYCLE_VAILD_TIME):
"""
:param CYCLE_VAILD_TIME: # 需要设置一个循环校验时间,不然放待校验ip的速度,还赶不上校验的速度
:return:
"""
# 连接数据库 等会校验存入进去的代理
conn = Reids_Client()
# 校验器对象
tester = VaildityTester()
# 循环校验
while True:
print('\033[1;30m{}\033[0m'.format('-' * 30 + ' Scheduler 调度器启动(控制调度器、添加器 核心CPU) ' + '-' * 29))
http_parameters, https_parameters = conn.queue_len()
for i in [http_parameters, https_parameters]:
count = int(i[0] * IP_CEHCK_COUNT) # 代理池存放IP数量
types = i[2] # 代理池为什么类型的代理
print('准备校验的代理IP数量', count)
if count == 0:
time.sleep(cycle)
break
print('' * 20 + '取之前长度', conn.queue_len())
proxies = conn.get(count=count, types=types)
print('取之后长度', conn.queue_len())
print('取出待校验IP数量', len(proxies))
# 校验
tester.set_raw_proxies(proxies)
tester.verify_check_switch()
time.sleep(cycle)
@staticmethod
def check_pool_add(lower_threshold=LOWER_THRESHOLD,
upper_threshold=UPPER_THRESHOLD,
cycle=ADD_CYCLE_TIME):
"""
添加器开关
"""
conn = Reids_Client()
adder = PoolAdder(upper_threshold)
while True:
# (已有IP长度,IP类型)
http_parameters, https_parameters = conn.queue_len()
if http_parameters[1] != None:
if http_parameters[0] <= lower_threshold:
t1 = threading.Thread(target=adder.add_to_pool, args=(http_parameters[2],))
t1.start()
if https_parameters[1] != None:
if https_parameters[0] <= lower_threshold:
t2 = threading.Thread(target=adder.add_to_pool, args=(https_parameters[2],))
t2.start()
# if http_parameters[1] != None:
# if http_parameters[0] <= lower_threshold:
# adder.add_to_pool(types=http_parameters[2]) # 195
# if https_parameters[1] != None:
# if https_parameters[0] <= lower_threshold:
# adder.add_to_pool(types=https_parameters[2])
time.sleep(cycle)
def run(self):
# Scheduler.valid_proxy()
# 创建多进程
p1 = Process(target=Scheduler.check_pool_add) # 从启动代理,到校验保存逻辑没有问题
p2 = Process(target=Scheduler.valid_proxy)
p1.start()
p2.start()
# -*- coding: utf-8 -*-
# @Time : 2020-12-28 11:19
# @Author : XuGuangJun
# @FileName: scheduler.py.py
# @Software: PyCharm
import time, threading
from db import Reids_Client
from getter import IpProxyGetter
import re
from multiprocessing import Process
from settings import CYCLE_VAILD_TIME, LOWER_THRESHOLD, UPPER_THRESHOLD, ADD_CYCLE_TIME, IP_LIVE, IP_CEHCK_COUNT
import json
import logging
"""
功能:调度器
包含:
---添加器:从代理服务器获取IP,传给校验器
---校验器:校验IP是否过期,校验成功存储到redis [可扩展性]
"""
class VaildityTester(object):
'''
校验器
'''
def __init__(self):
# 设为私有,不允许外部调用,存放待校验ip
self.__raw_proxies = []
def set_raw_proxies(self, proxiies):
"""
set_raw_proxies 集原料代理
放入未校验ip
:param proxies: 未校验ip 从getter 中传入待校验ip
:return:
"""
self.__raw_proxies = proxiies
# 数据库连接-->创建的方式会影响程序性能--》用的时候创建
self.__conn = Reids_Client() # redis数据库对象
def get_ProxyPararmeter(self, ProxyParameter, ip_live=IP_LIVE):
"""
获取代理校验参数
ProxyParameter:{'ip': ip, 'expire_time': i['expire_time'], 'types': 'https'}
"""
# proxy是从redis取出,是bytes类型,所以无法判断,进行转换
if isinstance(ProxyParameter, bytes):
# 转换为字符串
ProxyParameter = ProxyParameter.decode('utf-8')
"""
这里有2个逻辑,getter下载器yield过来的为dcit
进行校验的为str类型
"""
if type(ProxyParameter) == str:
ProxyParameter = json.loads(re.sub(r"'", '"', ProxyParameter))
now_time = int(time.time())
# 将其转换为时间戳
timeArray = time.strptime(ProxyParameter['expire_time'], "%Y-%m-%d %H:%M:%S")
Expiration_timeStamp = int(time.mktime(timeArray))
# 判断是否过期,并且存储
if (Expiration_timeStamp - ip_live) > now_time:
if ProxyParameter['types'] == 'http':
self.__conn.put(str(ProxyParameter), types='http') # redis当中存储
if ProxyParameter['types'] == 'https':
self.__conn.put(str(ProxyParameter), types='https') # redis当中存储
print('校验成功代理:', ProxyParameter)
else:
print('\033[1;31m已过期代理:{}\033[0m'.format(ProxyParameter))
def verify_check_switch(self):
"""
校验器开关
"""
print('\033[1;30m{}\033[0m'.format('-' * 23 + ' VaildityTester 校验器启动(校验内存中IP/redis中的IP,并且存储) ' + '-' * 23))
for ProxyParameter in self.__raw_proxies:
self.get_ProxyPararmeter(ProxyParameter)
class PoolAdder(object):
"""
添加器
"""
def __init__(self, threshold=UPPER_THRESHOLD):
# 创建存入redis数据库中最大代理池数量阈值 内置条件
self.__threshold = threshold # 60
# 创建开关实例
self.__tester = VaildityTester()
# 创建redis数据库对象实例
self.__conn = Reids_Client()
# 创建getter实例
self.__getter = IpProxyGetter()
def is_over_threshold(self, types):
"""
判断代理池中代理的数量是否达到最大值
:return True: 超过阈值
"""
if int(self.__conn.queue_len(types=types)) >= self.__threshold:
return True
return False
def add_to_pool(self, types):
"""
添加代理 添加到篮子
:return:
"""
print('\033[1;30m{}\033[0m'.format('-' * 28 + ' PoolAdder 添加器启动(代理商获得IP 存入内存 启动校验) ' + '-' * 28))
# 循环获取ip (获取材料)
while True:
if self.is_over_threshold(types=types): # 判断是否低于阈值
print(f'目前代理池的数量为限定{UPPER_THRESHOLD},不需要下载IP')
break
proxy_count = 0
for _ in self.__getter.__Protocol_Func__:
# 判断是那种类型
if types == 'http':
proxies = self.__getter.get_raw_proxies('protocol_http')
if len(proxies) > 0:
proxy_count += len(proxies)
# 使用校验器校验,是否过期
self.__tester.set_raw_proxies(proxies) # 放入材料
self.__tester.verify_check_switch() # 开启机器(开启校验方法)
if types == 'https':
proxies = self.__getter.get_raw_proxies('protocol_https')
if len(proxies) > 0:
proxy_count += len(proxies)
# 使用校验器校验,是否过期
self.__tester.set_raw_proxies(proxies) # 放入材料
self.__tester.verify_check_switch() # 开启机器(开启校验方法)
if proxy_count == 0: #
# 判出一个运行时间的异常
raise RuntimeError('获取代理时间过快,可忽略此异常!')
class Scheduler(object):
"""
调度器
"""
# 1、循环校验过程 不断从带池中头部获取一片,做定期检查
@staticmethod
def valid_proxy(cycle=CYCLE_VAILD_TIME):
"""
:param CYCLE_VAILD_TIME: # 需要设置一个循环校验时间,不然放待校验ip的速度,还赶不上校验的速度
:return:
"""
# 连接数据库 等会校验存入进去的代理
conn = Reids_Client()
# 校验器对象
tester = VaildityTester()
# 循环校验
while True:
print('\033[1;30m{}\033[0m'.format('-' * 30 + ' Scheduler 调度器启动(控制调度器、添加器 核心CPU) ' + '-' * 29))
http_parameters, https_parameters = conn.queue_len()
for i in [http_parameters, https_parameters]:
count = int(i[0] * IP_CEHCK_COUNT) # 代理池存放IP数量
types = i[2] # 代理池为什么类型的代理
print('准备校验的代理IP数量', count)
if count == 0:
time.sleep(cycle)
break
print('' * 20 + '取之前长度', conn.queue_len())
proxies = conn.get(count=count, types=types)
print('取之后长度', conn.queue_len())
print('取出待校验IP数量', len(proxies))
# 校验
tester.set_raw_proxies(proxies)
tester.verify_check_switch()
time.sleep(cycle)
@staticmethod
def check_pool_add(lower_threshold=LOWER_THRESHOLD,
upper_threshold=UPPER_THRESHOLD,
cycle=ADD_CYCLE_TIME):
"""
添加器开关
"""
conn = Reids_Client()
adder = PoolAdder(upper_threshold)
while True:
# (已有IP长度,IP类型)
http_parameters, https_parameters = conn.queue_len()
if http_parameters[1] != None:
if http_parameters[0] <= lower_threshold:
t1 = threading.Thread(target=adder.add_to_pool, args=(http_parameters[2],))
t1.start()
if https_parameters[1] != None:
if https_parameters[0] <= lower_threshold:
t2 = threading.Thread(target=adder.add_to_pool, args=(https_parameters[2],))
t2.start()
# if http_parameters[1] != None:
# if http_parameters[0] <= lower_threshold:
# adder.add_to_pool(types=http_parameters[2]) # 195
# if https_parameters[1] != None:
# if https_parameters[0] <= lower_threshold:
# adder.add_to_pool(types=https_parameters[2])
time.sleep(cycle)
def run(self):
# Scheduler.valid_proxy()
# 创建多进程
p1 = Process(target=Scheduler.check_pool_add) # 从启动代理,到校验保存逻辑没有问题
p2 = Process(target=Scheduler.valid_proxy)
p1.start()
p2.start()
if __name__ == '__main__':
# adder = PoolAdder()
# adder.add_to_pool('http')
Scheduler().valid_proxy()
# -*- coding: utf-8 -*-
# @Time : 2020-12-28 10:19
# @Author : XuGuangJun
# @FileName: settings.py
# @Software: PyCharm
# ---------------------- redis 配置 -----------------------
HOST = 'localhost'
PORT = 6380
PASSWORD = ''
PROXY_NAME = 'proxies'
HTTP_PROXY_NAME = 'http_proxies' # HTPP IP存储
HTTPS_PROXY_NAME = 'https_proxies' # HTTPS IP存储
# -------------------- 代理商API接口 -----------------------
HTTP_URL = 'http://webapi.http.zhimacangku.com/getip?num=20&type=2&pro=0&city=0&yys=0&port=1&time=1&ts=1&ys=0&cs=0&lb=1&sb=0&pb=45&mr=1®ions='
HTTPS_URL = 'http://webapi.http.zhimacangku.com/getip?num=20&type=2&pro=0&city=0&yys=0&port=11&time=1&ts=1&ys=0&cs=0&lb=1&sb=0&pb=45&mr=1®ions='
# HTTPS_URL = None # 不获得https类型
# ------------------- Scheduler 调度器配置 -------------------
TEST_TIME_OUT = 10 # 测试单个代理的超时时长
CYCLE_VAILD_TIME = 60 # 循环校验时间
ADD_CYCLE_TIME = 60 # 循环添加时间
LOWER_THRESHOLD = 20 # 代理池IP数量最小值
UPPER_THRESHOLD = 60 # 代理池IP数数量最大值
IP_LIVE = 120 # 代理API过期时间,实际上应当提前,避免IP失效了,造成请求接口响应失败误判为封禁,网络超时,数据丢失等
IP_CEHCK_COUNT = 0.3 # 每次校验10%
/
进入首页/proxy_index
代理池首页/add_ip
添加IP表名单 (付费代理都需要添加白名单功能)/get_http
获取http类型IP/get_https
获取https类型IP# -*- coding: utf-8 -*-
# @Time : 2020-12-28 18:08
# @Author : XuGuangJun
# @FileName: api.py
# @Software: PyCharm
from flask import Flask, g, render_template,request
from db import Reids_Client
import json
import re
import requests
app = Flask(__name__)
def get_conn():
if not hasattr(g, 'redis_client'):
g.redis_client = Reids_Client()
return g.redis_client
@app.route('/')
def index():
print('访问者的IP:',request.remote_addr)
print(request.user_agent)
return render_template("index.html", **locals())
@app.route('/proxy_index')
def proxy_index():
return render_template("proxy_index.html", **locals())
@app.route('/add_ip',methods=['GET','POST'])
def add_ip():
data = request.form
ip = data.get('ip')
if type(ip) == str:
if len(ip) < 11:
res = '请输入正确ip地址!'
return render_template("add_ip.html", **locals())
print('成功')
api_url = 'http://wapi.http.linkudp.com/index/index/save_white?neek=253760&appkey=249595af6047611a7f0136c55508857d&white=' +ip
print(api_url)
res = requests.get(url=api_url).text
return render_template("add_ip.html", **locals())
return render_template("add_ip.html", **locals())
@app.route('/get_http')
def get_http():
res = str(get_conn().pop(types='http'))
res = json.loads(re.sub(r"'", '"', res))
res['use_num'] = str(int(res['use_num']) + 1)
get_conn().lput(str(res), types=res['types'])
# return render_template("http_show.html", **locals())
return res
@app.route('/get_https')
def get_https():
res = str(get_conn().pop(types='https'))
res = json.loads(re.sub(r"'", '"', res))
res['use_num'] = str(int(res['use_num']) + 1)
get_conn().lput(str(res), types=res['types'])
# return render_template("https_show.html", **locals())
return res
@app.route('/count')
def count():
http_parameters, https_parameters = get_conn().queue_len()
# ((54, 'http_proxies', 'http'), (56, 'https_proxies', 'https'))
http_proxy_len = http_parameters[0]
https_proxy_len = https_parameters[0]
return render_template('show.html', **locals())
启动前要先启动redis服务
创建 run方法,一键启动,0.0.0.0 局域网用户都能访问
# -*- coding: utf-8 -*-
# @Time : 2020-12-28 18:10
# @Author : XuGuangJun
# @FileName: run.py
# @Software: PyCharm
from scheduler import Scheduler
from api import app
def main():
s = Scheduler()
s.run()
app.run(host='0.0.0.0',port=5000,debug=True)
if __name__ == '__main__':
main()
完成功能如下
另外一个页面,没有说明的
获取一个http类型IP
查看可用IP数量