搭建免费IP代理池

声明

本文章中所有内容仅供学习交流,不可用于任何商业用途和非法用途,否则后果自负,如有侵权,请联系作者立即删除!由于本人水平有限,如有理解或者描述不准确的地方,还望各位大佬指教!!


搭建代理池思路

思路来源:崔庆才大佬的爬虫书

代理从何而来

        用爬虫抓取各大代理网站

采集到的IP我们得将它存储起来

        Mysql:它当然可以存储IP,可是它也有它的局限性,Mysql不能去重,因为有时我们采集到的IP可能一样,还有一个问题就是Mysql查询效率低
        MongoDB:也可以存储IP,但它也不能去重
        Redis:最合适,首先它的查询效率最高,还有良好的去重的集合(zset);zset有一个特性,他有一个分值(score),我们可以通过控制分值的高低就可以将稳定性高的IP取出来,从而提高免费IP的可用性

能验证IP的有效性

        先将每个IP定一个初始分值(50),然后对每个IP都进行校验,如果这个IP可用那么就将这个IP的分值拉满(100),如果不可用就进行扣分(10),直到IP变成0分,就将这个IP删除。

思路理清了,接下来就是如何写程序了

        采集:写爬虫抓取IP,将IP存储到Redis
        校验:从Redis中取出IP,用IP简单发送一个请求,如果可以正常返回,证明该IP可用
        提供:写api接口,将可用的IP提供给用户
如果我们按照单线程去完成上面的步骤,就有局限性,只有每次将IP提供给用户,才可以继续采集IP,而我们希望的是这三个步骤互不影响,不管采集、校验还是给用户提供IP,都应该是一直进行,在提供IP的时候也可以继续采集、校验。三个独立的程序,我们就可以用多进程。

Redis

 - 连接Redis
 - zset存储 判断IP存不存在,不存在就新增 
 - 查询所有IP(校验IP时要用到)  
 - 将分值拉满(IP可用)
 - 将分值降低(IP不可用)
 - 查询可用的IP 先给满分的,没有满分的给51-99分的

# redis的各种操作
    from redis import Redis
    from settings import *
    class ProxyRedis:

    # 连接redis
    def __init__(self):
        self.red = Redis(
            host=REDIS_HOST,
            port=REDIS_PORT,
            db=REDIS_DB,
            password=REDIS_PASSWORD,
            decode_responses=True
        )

    # 存储ip
    def add_proxy_ip(self, ip):
        # 判断是否有ip
        if not self.red.zscore(REDIS_KEY, ip):
            self.red.zadd(REDIS_KEY, {ip: DEFAULT_SCORE})
            print("采集到了IP地址了", ip)
        else:
            print("采集到了IP地址了", ip, "但是已经存在")

    # 查询所有ip
    def get_all_proxy(self):
        return self.red.zrange(REDIS_KEY, 0, -1)

    # 将分值拉满
    def set_max_score(self, ip):
        self.red.zadd(REDIS_KEY, {ip: MAX_SCORE})

    # 降低分值
    def reduce_score(self, ip):
        # 查询分值
        score = self.red.zscore(REDIS_KEY, ip)
        # 如果有分值,扣分
        if score > 0:
            self.red.zincrby(REDIS_KEY, -10, ip)
        else:  # 分值没有则删除
            self.red.zrem(REDIS_KEY, ip)

    # 查询可用ip
    def get_avail_proxy(self):
        lis = []
        ips = self.red.zrangebyscore(REDIS_KEY, MAX_SCORE, MAX_SCORE, 0, -1)
        if ips:
            lis.append(ips)
            return lis
        else:
            ips = self.red.zrangebyscore(REDIS_KEY, DEFAULT_SCORE + 1, MAX_SCORE - 1, 0, -1)
            if ips:
                lis.append(ips)
                return lis
            else:
                print("没有可用ip")
                return None

采集ip

爬取这些网站很简单,基本都没有什么反爬,页面也都差不多,直接用xpath解析就可以得到想要的IP

    from proxy_redis import ProxyRedis
    import requests
    import time
    from lxml import etree
    from setting import *
    from urllib.parse import urljoin
    import urllib3

    urllib3.disable_warnings()

    headers = {"user-agent": 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36'}

    # 采集代理
    def get_ip(red, url_info):
        url = url_info['url']
        resp = requests.get(url, headers=headers, verify=False, timeout=10)
        resp.encoding = 'utf-8'
        tree = etree.HTML(resp.text)
        trs = tree.xpath(url_info['list_xpath'])
        for tr in trs:
            ip = tr.xpath(url_info['ip_xpath'])  # ip地址
            port = tr.xpath(url_info['port_xpath'])  # 端口
            if not ip:
                continue
            ip = ip[0].replace('\t', '').replace('\n', '').replace(' ', '')
            port = port[0].replace('\t', '').replace('\n', '').replace(' ', '')
            proxy_ip = ip + ":" + port
            red.add_proxy_ip(proxy_ip, url_info['name'])  # 增加ip地址

    def run():
        red = ProxyRedis()  # 创建redis存储
        while True:
            try:
                for url_info in url_infos:
                    get_ip(red, url_info)  # 采集代理
                get_xiaoshu(red)
                get_66(red)
                get_proxylist(red)
                get_zdaye(red)
            except Exception as e:
                logger.error(f"抓取ip出错了--{e}")
            time.sleep(300)  # 每分钟跑一次

    if __name__ == '__main__':
        run()

校验ip可用性

 - 查询所有的IP
 - 每一个IP都发送一个请求,可用分值拉满,不用可扣分
这里如果我们采集的IP比较多的话,用单线程就比较慢了,所以为了提高效率,这里我采用协程

    from proxy_redis import ProxyRedis
    from settings import *
    import asyncio
    import aiohttp
    import time

    async def verify_one(ip, sem, red):
        print(f"开始检测{ip}")
        timeout = aiohttp.ClientTimeout(total=10)  # 设置超时时间,超过10秒就报错
        try:
            async with sem:
                async with aiohttp.ClientSession() as session:
                    async with session.get("http://www.baidu.com/", proxy="http://" + ip, timeout=timeout) as resp:  # 简单发送一个请求
                        page_source = await resp.text()
                        if resp.status in [200, 302]:  # 验证状态码
                            # 将分值拉满
                            red.set_max_score(ip)
                            print(f"检测到{ip}是可用的")
                        else:
                            red.reduce_score(ip)
                            print(f"检测到{ip}是不可用的, 扣10分")
        except Exception as E:
            print("ip检验时出错了", E)
            red.reduce_score(ip)
            print(f"检测到{ip}是不可用的, 扣10分")


    async def main(red):
        # 查询全部ip
        all_proxy = red.get_all_proxy()
        sem = asyncio.Semaphore(SEM_COUNT)  # 控制并发量
        tasks = []
        for ip in all_proxy:
            tasks.append(asyncio.create_task(verify_one(ip, sem, red)))
        if tasks:
            await asyncio.wait(tasks)


    def run():
        red = ProxyRedis()
        time.sleep(10)
        while True:
            try:
                asyncio.run(main(red))
                time.sleep(100)
            except Exception as e:
                print("校验时报错了", e)
                time.sleep(100)

    if __name__ == '__main__':
        run()

使用flask写接口

    from proxy_pool import proxy_redis
    from flask import Flask, request, jsonify
    import random

    app = Flask(__name__)
    red = proxy_redis.ProxyRedis()

    @app.route("/get_ip", methods=["GET"])
    def dispose():
        ip_list = red.get_avail_proxy()
        if ip_list:
            return random.choice(ip_list)   # 返回给客户端
        else:
            return '没有可用ip'
    if __name__ == '__main__':
        app.run(host="0.0.0.0", port=3000)
**启动进程**

    from ip_api import run as api_run
    from ip_collection import run as col_run
    from ip_verify import run as ver_run
    from multiprocessing import Process

    def run():
        # 启动三个进程
        p1 = Process(target=api_run)
        p2 = Process(target=col_run)
        p3 = Process(target=ver_run)

        p1.start()
        p2.start()
        p3.start()

    if __name__ == '__main__':
        run()

配置文件

    # 配置文件

    # proxy_redis
    # redis主机ip地址
    REDIS_HOST = "127.0.0.1"
    # redis端口号
    REDIS_PORT = 6379
    # redis数据库编号
    REDIS_DB = 2
    # redis的密码
    REDIS_PASSWORD = "123456"

    # redis的key
    REDIS_KEY = "proxy_ip"

    # 默认的ip分值
    DEFAULT_SCORE = 50
    # 满分
    MAX_SCORE = 100

    # ip_verify
    # 一次检测ip的数量
    SEM_COUNT = 30

代理池获取链接:https://www.aliyundrive.com/s/Yi17DfjWUAt

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值