Python实现IP代理池(MondoDB和Flask实现)

ProxiesPool IP代理池

MongoDB和flask实现的一个IP代理池

源代码地址,直接下载既可以使用,已包含依赖包

https://github.com/FelixZFB/ProxiesPool

基本思路:

  • 获取代理IP: 爬取网站的免费代理。比如西刺、快代理之类有免费代理的网站, 但是这些免费代理大多数情况下都是不好用的,所以比较靠谱的方法是购买付费代理。当然,如果你有更好的代理接口也可以自己接入。
  • 检测IP代理可用性: 因为免费代理大部分是不可用的,所以采集回来的代理IP不能直接使用,可以写检测程序不断的去用这些代理访问一个稳定的网站,看是否可以正常使用。
  • 存储代理IP: 存储的代理IP首先要保证代理不重复 , 要检测代理的可用情况,还要动态实时处理每个代理,本文利用来MongoDB存储,当然也可用其他方式存储。
  • 使用代理:最简单的办法就是用 API 来提供对外服务的接口 。

实现逻辑:

- 1.爬取免费代理IP网站(西刺代理,快代理,66代理等)的高匿代理
- 2.检测出有效的高匿代理,存储到MongoDB数据库中
- 3.定时检测MongoDB数据库已有IP代理是否有效,进行更新删除
- 4.使用flask的api接口调用IP代理池中的IP代理

在这里插入图片描述

IP代理池启动方式:

- 1.启动本地MongoDB数据库
- 2.运行main.py
- 3.外部api调用

外部调用方式:

参考api_demo.py

import requests
PROXY_POOL_URL = 'http://127.0.0.1:5000/one'  # one proxy
PROXIES_POOL_URL = 'http://127.0.0.1:5000/all'  # all proxies

下面方几个主要代码,完整代码查看源码

main.py 主程序(调度器)

from multiprocessing import Process
from Crawler.check_crawl_ip import CheckIp, CrawlIp
from api import run

def proxy_run():
    # 数据库中ip检测进程
    check_process = Process(target=CheckIp().check)
    # 爬取ip代理的进程
    crawl_process = Process(target=CrawlIp().crawl)
    # api接口进程,用于从数据库中取出一个或者全部ip代理
    run_process = Process(target=run)

    # 启动所有进程
    check_process.start()
    crawl_process.start()
    run_process.start()

    # 等待所有进程结束
    check_process.join()
    crawl_process.join()
    run_process.join()


if __name__ == '__main__':
    proxy_run()

get_proxy.py 下载模块(获取代理)

import requests
import chardet
import traceback
import user_agent
from lxml import etree
from requests.exceptions import ConnectionError


class GetProxy():
    def __init__(self):
        self.headers = {"User-Agent": user_agent.generate_user_agent()}

    # 判断提供ip代理网站是否有效,返回网页html文档
    def parse_url(self, url):
        try:
            response = requests.get(url, headers=self.headers)
            response.encoding = chardet.detect(response.content)["encoding"]
            if response.status_code == 200:
                return response.text
            else:
                return None
        except ConnectionError:
            print("Error.")
        return None

    # 获取西刺代理网站免费代理ip
    def xici_proxy(self):
        # 只获取网站高匿代理前20页的代理
        xici_list = list()
        for i in range(1, 20):
            url = "https://www.xicidaili.com/nn/{}".format(i)
            response = self.parse_url(url)
            # 上面的response类型有时候是NoneType,有些是str,下面使用str(response)
            html = etree.HTML(str(response), etree.HTMLParser())
            # 所有的ip和port都是放在属性为ip_list标签下的tr标签里面
            # ip_list属性唯一,下面两种方式都是选取所有属性id='ip_list'的标签
            ip_list = html.xpath("//table[@id='ip_list']/tr/td[2]/text()")
            port_list = html.xpath("//*[@id='ip_list']/tr/td[3]/text()")

            # ip和port生成一个一一对应的元组列表,然后取出
            for ip, port in zip(ip_list, port_list):
                proxy = ip + ":" + port
                proxy = {"proxy": proxy}  # 返回的代理是字典的格式,方便直接存储到mongodb数据库中
                # yield proxy
                xici_list.append(proxy)
        return xici_list

    # 获取快代理网站免费代理ip
    def kuai_proxy(self):
        # 只获取网站高匿代理前20页的代理
        kuai_list = list()
        for i in range(1, 20):
            url = "https://www.kuaidaili.com/free/inha/{}/".format(i)
            response = self.parse_url(url)
            # 上面的response类型有时候是NoneType,有些是str,下面使用str(response)
            html = etree.HTML(str(response), etree.HTMLParser())
            # 所有的ip和port都是放在属性为list的div/table/tbody下的tr标签里面
            ip_list = html.xpath("//div[@id='list']/table/tbody/tr/td[1]/text()")
            port_list = html.xpath("//div[@id='list']/table/tbody/tr/td[2]/text()")

            # ip和port生成一个一一对应的元组列表,然后取出
            for ip, port in zip(ip_list, port_list):
                proxy = ip + ":" + port
                proxy = {"proxy": proxy} # 返回的代理是字典的格式,方便直接存储到mongodb数据库中
                # yield proxy
                kuai_list.append(proxy)
        return kuai_list

    # 获取快代理网站免费代理ip
    def liuliu_proxy(self):
        # 只获取网站高匿代理前20页的代理
        # liuliu_list = list()
        for i in range(1, 20):
            url = "http://www.66ip.cn/{}.html".format(i)
            response = self.parse_url(url)
            # 上面的response类型有时候是NoneType,有些是str,下面使用str(response)
            html = etree.HTML(str(response), etree.HTMLParser())
            # 所有的ip和port都是放在属性为list的div/table/tbody下的tr标签里面,列表第一个元素是标题栏,去除掉
            ip_list = html.xpath("//div[@class='containerbox boxindex']/div[1]/table[1]//tr/td[1]/text()")[1:]
            port_list = html.xpath("//div[@class='containerbox boxindex']/div[1]/table[1]//tr/td[2]/text()")[1:]

            # ip和port生成一个一一对应的元组列表,然后取出
            for ip, port in zip(ip_list, port_list):
                proxy = ip + ":" + port
                proxy = {"proxy": proxy}  # 返回的代理是字典的格式,方便直接存储到mongodb数据库中
                yield proxy
                # liuliu_list.append(proxy)
        # return liuliu_list

    # 自己可以扩充代理网站
    def other_proxy(self):
        pass


if __name__ == '__main__':
    res = GetProxy().liuliu_proxy()
    print(res)
    for i in res:
        print(type(i))
        print(i)

mongo_db.py 存储模块(存储代理到数据库)

import pymongo
from pymongo.errors import DuplicateKeyError


class MongoDB():

    def __init__(self):
        # 连接mongodb服务器,先启动mongodb服务器和客户端
        self.client = pymongo.MongoClient(host="localhost", port=27017)
        # 连接ProxiesPool数据库,可以先在mongodb中创建ProxiesPool数据库,集合插入数据时会自动创建
        self.db = self.client['ProxiesPool']
        # 连接ProxiesPool数据库下的proxies集合
        self.proxies = self.db['proxies']
        # 给proxy字段创建一个新的索引,加快查询的速度
        self.proxies.ensure_index('proxy', unique=True)

    # 插入数据
    def insert(self, proxy):
        try:
            self.proxies.insert(proxy)
            print("插入成功:{}".format(proxy))
        except DuplicateKeyError:
            pass

    # 删除数据
    def delete(self, conditions):
        self.proxies.remove(conditions)
        print("删除成功:{}".format(conditions))

    # 更新数据
    def update(self, conditions, values):
        self.proxies.update(conditions, {"$set": values})
        print("更新成功:{},{}".format(conditions, values))

    # 取出所有的数据,count是check_crawl_ip获取到的ip代理数量
    def get(self, count, conditions=None):
        conditions = conditions if conditions else {}
        count = int(count)
        items = self.proxies.find(conditions)  # conditions=None,即默认查找所有的proxies集合下所有的文档
        # 取出数据,按delay进行排序,延时小的放在列表前面,用的时候可以先拿出来
        # items = self.proxies.find(conditions, limit=count).sort("delay", pymongo.ASCENDING)
        items = list(items)
        return items

    # 统计数据库中代理个数
    def get_count(self):
        return self.proxies.count({})  # {}条件为空,即统计全部数据


if __name__ == '__main__':
    mongodb = MongoDB()
    print(mongodb.get(3))

test_proxy.py 检测模块(检测代理是否有效)

import requests
import time
from requests.exceptions import ProxyError, ConnectionError
from MongoDB.mongo_db import MongoDB
from multiprocessing.pool import ThreadPool


class TestIp():

    def test_all(self, proxy_list, method):
        # 进程池中同时最多16个进程
        pool = ThreadPool(16)
        # 向进程池中添加任务
        for proxy in proxy_list:
            pool.apply_async(self.test_one, args=(proxy, method))
        # 关闭进程池,不在接受新的任务
        pool.close()
        # 等待所有子进程结束
        pool.join()

    def test_one(self, proxy, method):
        url = 'https://www.baidu.com'
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'
        }
        proxies = {
            'http': 'http://' + proxy['proxy'],
            'https': 'http://' + proxy['proxy']
        }
        try:
            start_time = time.time()
            resp = requests.get(url, headers=headers, proxies=proxies, timeout=5, verify=True)
            # 记录ip代理请求用时
            delay = round(time.time() - start_time, 2)
            #
            if resp.status_code == 200:
                # 把delay加入到proxy字典中
                proxy['delay'] = delay
                if method == 'insert':
                    # 插入代理到数据库
                    MongoDB().insert(proxy)
                elif method == 'check':
                    MongoDB().update({'proxy': proxy['proxy']}, {'delay': proxy['delay']})
            else:
                print("无效ip:{}".format(proxy))
                if method == 'check':
                    MongoDB().delete({'proxy': proxy['proxy']})
        except (ProxyError, ConnectionError):
            print("无效ip:{}".format(proxy))
            if method == 'check':
                MongoDB().delete({'proxy': proxy['proxy']})
        except Exception:
            # traceback.print_exc()
            pass

api.py 外部API接口

import flask
import json
from MongoDB.mongo_db import MongoDB
import random

app = flask.Flask(__name__)


# 从数据库中获取一个ip代理
@app.route('/one')
def get_one():
    proxies = MongoDB().get(1)
    # 所有代理数量减去一个,然后从result列表随机取出1个,MongoDB().get条件为None,取出的是所有的ip代理
    result = [proxy['proxy'] for proxy in proxies]
    x = random.randint(0, MongoDB().get_count() - 1)
    # 返回json格式的类似字典的字符串
    return json.dumps(dict(proxy=result[x]) )

# 从数据库中获取所有的ip代理
@app.route('/all')
def get_all():
    #  http://127.0.0.1:5000/many?count=2
    # args = flask.request.args  # 参数提交
    proxies = MongoDB().get(1)
    result = [proxy['proxy'] for proxy in proxies]
    # x = random.randint(1,MongoDB().get_count()-1)

    # 返回json格式的类似列表的字符串
    return json.dumps(result)


@app.route('/delete')
def delete():
    args = flask.request.args
    MongoDB().delete({'proxy': args['proxy']})
    return '删除成功:{}'.format(args)


def run():
    app.run()

源代码地址,直接下载既可以使用,已包含依赖包

https://github.com/FelixZFB/ProxiesPool

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章