记录一个免费代理池的维护,主要包含四个模块:
获取模块:主要负责从各个免费代理网站提取出最新发布的免费代理,获取到本地并解析
存储模块:负责将获取模块获取到的proxy存储至redis数据库
检测模块:负责检测redis数据库中proxy的可用代理可不可用代理,并赋以权重
调度模块:负责将获取模块、存储模块和检测模块关联,并封装
主要涉及知识点:
- 元类
- python操作redis数据库,redis库的使用
- requests库的使用
- pyquery的使用
- aiohttp异步http框架的简单使用
- 多线程和多进程
存储模块
# -*- coding: utf-8 -*-
"""
__author__ = 'bingo'
__date__ = '2019/9/7'
# code is far away from bugs with the god animal protecting
I love animals. They taste delicious.
┏┓ ┏┓
┏┛┻━━━┛┻┓
┃ ☃ ┃
┃ ┳┛ ┗┳ ┃
┃ ┻ ┃
┗━┓ ┏━┛
┃ ┗━━━┓
┃ 神兽保 ┣┓
┃ 永无BUG┏┛
┗ ┓┏━┳┓┏┛
┃┫┫ ┃┫┫
┗┻┛ ┗┻┛
"""
import random
import asyncio
import requests
import time
import redis
import aiohttp
from pyquery import PyQuery as pq
from redis import ResponseError
from requests import ConnectTimeout
from concurrent.futures import ThreadPoolExecutor
from multiprocessing import Process
from flask import Flask
# 存储模块
class ProxyRedisClient(object):
__INIT__SCORE__ = 10
__MAX__SCORE__ = 100
__REDIS__KEY__ = "proxies"
def __init__(self):
self.host = "localhost"
self.port = 6379
self.password = None
self.db = redis.StrictRedis(host=self.host, port=self.port, password=self.password, decode_responses=True,
db=10)
def add(self, proxy, score=__INIT__SCORE__):
"""
代理池数据库添加代理
:param proxy: 代理ip
:param score: 分数,初始10分
:return: True or False
"""
# zadd()向有序集合中添加一个元素
if not self.exists(proxy):
self.db.zadd(self.__REDIS__KEY__, {proxy: score})
return True
else:
print("代理【{}】重复".format(proxy))
return False
def max(self, proxy, score=__MAX__SCORE__):
"""
代理池数据库添加代理,如果已存在,则更新为最大值
:param proxy: 代理ip
:param score: 分数,最大值
:return: True or False
"""
return self.db.zadd(self.__REDIS__KEY__, {proxy: score})
def random(self):
"""
返回一个随机代理,优先返回分数最高的
:return:
"""
# zrevrangebyscore()根据给定的键名和分数取键返回区间内元素,并由大到小排序
# zrevrange() 根据分数从大到小排序,并返回给定索引区间的元素,如此处返回前10个元素
result = self.db.zrevrangebyscore(self.__REDIS__KEY__, 100, 100)
if result:
return random.choice(result)
else:
result = self.db.zrevrange(self.__REDIS__KEY__, 0, 10)
if result:
return random.choice(result)
else:
raise ResponseError
def increase(self, proxy):
"""
分数+1
:return:
"""
# zscore() 通过键名key和value返回元素分数,不存在返回None
# zincrby() 通过键名key和value对对应元素分数加或者减
proxy_score = self.db.zscore(self.__REDIS__KEY__, proxy)
if not proxy_score:
return
if proxy_score < 100:
print("当前代理【%s】分数【%s】:+3" % (proxy, proxy_score))
self.db.zincrby(self.__REDIS__KEY__, 3, proxy)
else:
print("当前代理【%s】分数【%s】:已为最大分数" % (proxy, proxy_score))
pass
def decrease(self, proxy):
"""
分数-1
:return:
"""
proxy_score = self.db.zscore(self.__REDIS__KEY__, proxy)
if not proxy_score:
return
if proxy_score > self.__INIT__SCORE__:
print("当前代理【%s】分数【%s】:-5" % (proxy, proxy_score))
self.db.zincrby(self.__REDIS__KEY__, -5, proxy)
else:
print("当前代理【%s】分数【%s】:移除" % (proxy, proxy_score))
self.db.zrem(self.__REDIS__KEY__, proxy)
def exists(self, proxy):
"""
判断代理是否存在代理池数据库中
:param proxy: 代理ip
:return: True or False
"""
# zscore()通过键名key和value返回元素分数,不存在返回None
r = self.db.zscore(self.__REDIS__KEY__, proxy)
if r:
return True
else:
return False
def count(self):
"""
获取代理池全部代理数量
:return:
"""
# zcount()根据键名和分数区间,返回区间内元素个数
return self.db.zcount(self.__REDIS__KEY__, 0, 100)
def all(self):
"""
获取代理池全部代理
:return:
"""
# zrangebyscore()根据给定的键名和分数取键返回区间内元素,并由小到大排序
return self.db.zrangebyscore(self.__REDIS__KEY__, 0, self.__MAX__SCORE__)
网友评论