实现一个免费的IP代理池

我将代理池程序设计为4个模块:存储模块获取模块检查模块接口模块。模块之间关系如下:

存储模块:我使用的是redis数据库存储,使用的是redis中的集合,集合内元素无序并且无重复。该存储模块的主要功能就是存入可用代理,删除不可用代理,随机提取可用代理,获取所有可用代理,获取当前可用代理数量。

获取模块:该模块主要功能是定时对网上的免费代理网站进行爬取。

检查模块:检查模块主要有两个功能:第一个是对获取模块爬取的代理进行检查,检查通过则允许调用存储模块存入数据库,如果检查不通过,这筛出掉该条代理。第二个是定时对数据库中的代理进行复查,如果通过,则保留,如果没有通过则删除该代理。

接口模块:建立Flask程序,对外提供接口,可以通过该接口随机获取可用代理,可用代理总数。

项目结构如下:

依次是:检查模块,配置文件,获取模块,执行文件,接口模块,存储模块。

存储模块代码

import redis

from config import REDIS_HOST, REDIS_PORT, REDIS_PASSWORD, REDIS_KEY


class RedisClient(object):
    def __init__(self):
        """初始化,建立redis连接"""
        self.db = redis.StrictRedis(host=REDIS_HOST, port=REDIS_PORT, password=REDIS_PASSWORD, decode_responses=True)

    def add_one(self, proxy):
        """添加代理"""
        if not self.db.sismember(REDIS_KEY, proxy):
            return self.db.sadd(REDIS_KEY, proxy)
        else:
            return None

    def del_one(self, proxy):
        """删除代理"""
        self.db.srem(REDIS_KEY, proxy)

    def get_one(self):
        """随机获取一个有效代理"""
        result = self.db.srandmember(REDIS_KEY, 1)
        if len(result) >= 1:
            return result[0]
        else:
            return None

    def count(self):
        """获取数量"""
        return self.db.scard(REDIS_KEY)

    def get_all(self):
        """获取全部代理"""
        return self.db.smembers(REDIS_KEY)

获取模块代码

from time import sleep

import requests
from lxml import etree

from config import KAIDAILI_PAGE_COUNT, XICI_PAGE_COUNT

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 5.1; U; en; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 9.50',
}


def get_page(url):
    """获取页面"""
    try:
        res = requests.get(url, headers=headers, timeout=5)
    except:
        return None
    else:
        if res.status_code == 200:
            return res.text
        else:
            return None


class Crawler:
    """获取免费代理类"""
    def crawl_xici(self, page_count=XICI_PAGE_COUNT):
        """获取西刺代理"""
        start_url = 'https://www.xicidaili.com/nn/{}'
        for page in range(1, page_count + 1):
            url = start_url.format(page)
            res = get_page(url)
            if res:
                html = etree.HTML(res)
                ips = html.xpath('//table[@id="ip_list"]/tr/td[2]/text()')
                ports = html.xpath('//table[@id="ip_list"]/tr/td[3]/text()')
                if len(ips) == len(ports):
                    for i in range(len(ips)):
                        proxy = ips[i] + ':' + ports[i]
                        yield proxy
            sleep(10)

    def crawl_kuaidaili(self, page_count=KAIDAILI_PAGE_COUNT):
        """获取快代理"""
        start_url = 'https://www.kuaidaili.com/free/inha/{}/'
        for page in range(1, page_count + 1):
            url = start_url.format(page)
            res = get_page(url)
            if res:
                html = etree.HTML(res)
                ips = html.xpath('//td[@data-title="IP"]/text()')
                ports = html.xpath('//td[@data-title="PORT"]/text()')
                if len(ips) == len(ports):
                    for i in range(len(ips)):
                        proxy = ips[i] + ':' + ports[i]
                        yield proxy
            sleep(10)

    def get_proxy(self):
        """迭代返回proxy"""
        for proxy in self.crawl_xici():
            yield proxy
        for proxy in self.crawl_kuaidaili():
            yield proxy


检查模块代码

import requests

from config import TEST_URL
from proxy_storage import RedisClient


class Checker:
    """检查代理类"""
    def __init__(self):
        """初始化"""
        self.redis = RedisClient()

    def check_proxy(self, proxy):
        """测试单个代理是否可用"""
        proxies = {
            "http": "http://" + proxy,
            "https": "https://" + proxy,
        }
        try:
            response = requests.get(TEST_URL, proxies=proxies, timeout=5)
        except:
            print('proxy请求失败:', proxy)
            return False
        else:
            if response.status_code == 200:
                print('proxy可用:', proxy)
                return True
            else:
                print('proxy不可用:', proxy)
                return False


    def review(self):
        """proxy复查"""
        try:
            proxies = self.redis.get_all()
            for proxy in proxies:
                res = self.check_proxy(proxy)
                if not res:
                    self.redis.del_one(proxy)
                    print('proxy复查不可用,已删除:', proxy)
        except Exception as e:
            print('测试器发生错误', e.args)

    def first_check(self, proxy):
        """proxy存入redis之前的首次检查"""
        res = self.check_proxy(proxy)
        return res

接口模块代码

from flask import Flask, g

from proxy_storage import RedisClient

__all__ = ['app']
app = Flask(__name__)


def get_conn():
    if not hasattr(g, 'redis'):
        g.redis = RedisClient()
    return g.redis


@app.route('/')
def index():
    """主页"""
    return '<h2>欢迎使用IP代理池!</h2>'


@app.route('/get_one')
def get_proxy():
    """获取随机可用代理"""
    conn = get_conn()
    proxy = conn.get_one()
    if proxy:
        return proxy
    else:
        return "暂无可用代理"


@app.route('/count')
def get_counts():
    """获取代理池总量"""
    conn = get_conn()
    return str(conn.count())

配置文件代码

# 检查proxy是否可用时使用的测试url
TEST_URL = 'http://www.baidu.com'
# TEST_URL = 'https://weixin.sogou.com/'

# 获取西刺代理的页数
XICI_PAGE_COUNT = 5
# 获取快代理的页数
KAIDAILI_PAGE_COUNT = 10

# 设置redis存储proxy上限
THRESHOLD = 100

# 每次获取proxy的间隔时长
GET_INTERVAL = 100
# 每次复查proxy的间隔时长
REVIEW_INTERVAL = 100

# redis配置
REDIS_HOST = 'localhost'
REDIS_PORT = 6379
REDIS_PASSWORD = '123456'
REDIS_KEY = 'my_proxies'

# API服务host和port
API_HOST = '127.0.0.1'
API_PORT = '8080'

执行文件代码

from time import sleep

from multiprocessing import Process

from check_proxy import Checker
from config import THRESHOLD, REVIEW_INTERVAL, GET_INTERVAL, API_HOST, API_PORT
from get_proxy import Crawler
from proxy_api import app
from proxy_storage import RedisClient


class Manager:
    """获取模块和检查模块的调用"""
    def __init__(self):
        """初始化"""
        self.redis = RedisClient()
        self.crawler = Crawler()
        self.tester = Checker()

    def is_over_threshold(self):
        """判断是否达到代理池限制"""
        if self.redis.count() >= THRESHOLD:
            return True
        else:
            return False

    def getter(self):
        """获取模块逻辑"""
        if not self.is_over_threshold():
            for proxy in self.crawler.get_proxy():
                res = self.tester.first_check(proxy)
                if res:
                    info = self.redis.add_one(proxy)
                    if not info:
                        print('该proxy已经存在:', proxy)
                    else:
                        print('成功存入proxy:', proxy)
                else:
                    print('proxy不可用,已剔除:', proxy)

    def reviewer(self):
        """检查模块逻辑"""
        self.tester.review()


manager = Manager()


class Scheduler:
    """调度器,程序运行逻辑实现"""
    def schedule_getter(self):
        """定时获取代理"""
        while True:
            print('开始获取proxy')
            manager.getter()
            sleep(GET_INTERVAL)

    def schedule_reviewer(self):
        """定时测试代理"""
        while True:
            print('开始审核proxy')
            manager.reviewer()
            sleep(REVIEW_INTERVAL)

    def schedule_api(self):
        """开启API"""
        print("api已开启")
        app.run(API_HOST, API_PORT)

    def run(self):
        """多进程运行代理池"""
        print('代理池开始运行')

        getter_process = Process(target=self.schedule_getter)
        getter_process.start()

        tester_process = Process(target=self.schedule_reviewer)
        tester_process.start()

        api_process = Process(target=self.schedule_api)
        api_process.start()


if __name__ == '__main__':
    scheduler = Scheduler()
    scheduler.run()

运行该代理池:

由于是获取的免费代理,所以很多代理都是不可用的,哈哈哈,毕竟是免费的。

redis数据库显示如下:

然后我们通过访问接口获取可用代理:

这样一个免费的IP代理池就完成了,哈哈哈。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
v4.7(2019-05-23)修正第二次创建代理提取架构后无法获取到代理的问题增加了两个新的函数`获取代理Ex()` `自压入代理Ex()`  '//详情请查看`例程2.e`增加了两个例程`例程1.e` `例程2.e`使用方法:1.  (编辑配置)1.1 编辑运行目录下的`Config_Proxy.ini`填写提取和验证参数并保存 1.2 如果程序目录下没有`Config_Proxy.ini`这个文件 请调用一次`代理智能提取_创建()`会自动生成在运行目录下1.3 你也可以用代码来配置这些信息 直接调用 `代理智能提取_置代理提取参数()` `代理智能提取_置代理验证参数()` `代理智能提取_置代理生命值()` `代理智能提取_置代理最长存活时间()`2.  (创建和获取状态)2.1 在执行工作前调用一次 `代理智能提取_创建()`  (比如: `启动按钮_被单击`)2.2 你可以搞个时钟/定时器来刷新代理智能提取模块的工作状态2.3 当你打算结束工作/工作完毕时记得执行 `代理智能提取_销毁()`3.  (提取代理)3.1 当你需要用代理时 直接调用`代理智能提取_获取代理()`即可得到一条代理3.2 没错 你可以在任意环境下调用`代理智能提取_获取代理()` (比如:在并发线程中调用)4.  (总结)如果你已修改好了提取配置 你其实可以很轻松的使用它 只要先创建 然后只管在任意线程获取代理即可 代理快不够了会自动提取补充就像下面这样创建()获取代理()'//线程A获取代理()'//线程B获取代理()'//线程C...销毁()--------------------------------------------Config_Proxy.ini 配置说明:触发补充阀值     : 当前剩余代理小于这个数值会触发自动提取并验证补充代理(0=自动(本次提取数量\5 5且>=5))提取地址         : 用于提取代理的API地址提取分隔符       :  提取后用这个分隔符来分割成多条 默认=\\r\\n(换行符) (本参数也支持正则匹配)是否正则分割     : 是否使用正则匹配 1=使用正则匹配(正则必须包含两个子匹配项1为地址2为端口) 0=使用分割文本匹配提取附加协议头   : 提取代理时附加的HTTP协议头 默认=空(默认协议头)提取间隔         : 两次提取最小间隔(毫秒) 为了防止提取API接口限制提取频繁冻结 默认=1000(1秒)----------------验证地址         : 用于验证代理是否有效的url 比如IP138 又比如百度  |如果想不验证提取的代理直接使用 请设置为:不验证是否UTF8解码     : 验证代理返回的网页内容是否进行UTF8解码 1=解码 0=不解码提取附加协议头   : 验证代理时附加的HTTP协议头 默认=空(默认协议头)验证特征         : 验证代理URL返回的内容里存在这个特征既是有效(比如验证地址是(百度) 特征可以是'百度一下')验证超时         : 验证代理超时 默认=12 (秒)尝试验证次数     : 尝试验证次数 默认=1 (次)代理生命值       : 提取的代理能被获取几次 默认=1 (次)  (比如采集东西的时候就可以设置10-50次)代理最长存活时间 : 提取的代理最长存活时间 0=不启用(默认) 大于0=启用(秒) 提取的代理过了这个时间后 将不会被提取直接被丢弃--------------------------------------------

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值