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

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