基於後端和爬蟲創建的代理ip池

来源:https://www.cnblogs.com/Max-message/archive/2022/07/05/16448795.html
-Advertisement-
Play Games

搭建免費的代理ip池 需要解決的問題: 使用什麼方式存儲ip 文件存儲 缺點: 打開文件修改文件操作較麻煩 mysql 缺點: 查詢速度較慢 mongodb 缺點: 查詢速度較慢. 沒有查重功能 redis --> 使用redis存儲最為合適 所以 -> 數據結構採用redis中的zset有序集合 ...


搭建免費的代理ip池


需要解決的問題:

  1. 使用什麼方式存儲ip

    • 文件存儲

      缺點: 打開文件修改文件操作較麻煩

    • mysql

      缺點: 查詢速度較慢

    • mongodb

      缺點: 查詢速度較慢. 沒有查重功能

    • redis --> 使用redis存儲最為合適

    所以 -> 數據結構採用redis中的zset有序集合

  2. 獲取ip的網站

  3. 項目架構???

項目架構

  1. 獲取api
  2. 篩選api
  3. 驗證api的有效性
  4. 提供api

項目結構圖

項目結構如下:

項目代碼

code文件夾

redis_proxy.py

# -*- encoding:utf-8 -*-
# @time: 2022/7/4 11:32
# @author: Maxs_hu
"""
這裡用來做redis中間商. 去控制redis和ip之間的調用關係
"""
from redis import Redis
import random


class RedisProxy:
    def __init__(self):
        # 連接到redis資料庫
        self.red = Redis(
            host='localhost',
            port=6379,
            db=9,
            password=123456,
            decode_responses=True
        )

    # 1. 存儲到redis中. 存儲之前需要提前判斷ip是否存在. 防止將已存在的ip的score抵掉
    # 2. 需要校驗所有的ip. 查詢ip
    # 3. 驗證可用性. 可用分值拉滿. 不可用扣分
    # 4. 將可用的ip查出來返回給用戶
    #       先給滿分的
    #       再給有分的
    #       都沒有分. 就不給

    def add_ip(self, ip):  # 外界調用並傳入ip
        # 判斷ip在redis中是否存在
        if not self.red.zscore('proxy_ip', ip):
            self.red.zadd('proxy_ip', {ip: 10})
            print('proxy_ip存儲完畢', ip)
        else:
            print('存在重覆', ip)

    def get_all_proxy(self):
        # 查詢所有的ip功能
        return self.red.zrange('proxy_ip', 0, -1)

    def set_max_score(self, ip):
        self.red.zadd('proxy_ip', {ip: 100})  # 註意是引號的格式

    def deduct_score(self, ip):
        # 先將分數查詢出來
        score = self.red.zscore('proxy_ip', ip)
        # 如果有分值.那就扣一分
        if score > 0:
            self.red.zincrby('proxy_ip', -1, ip)
        else:
            # 如果分值已經扣的小於0了. 那麼可以直接刪除了
            self.red.zrem('proxy_ip', ip)

    def effect_ip(self):
        # 先將ip通過分數篩選出來
        ips = self.red.zrangebyscore('proxy_ip', 100, 100, 0, -1)
        if ips:
            return random.choice(ips)
        else:  # 沒有滿分的
            # 將九十分以上的篩選出來
            ips = self.red.zrangebyscore('proxy_ip', 11, 99, 0, -1)
            if ips:
                return random.choice(ips)
            else:
                print('無可用ip')
                return None

ip_collection.py

# -*- encoding:utf-8 -*-
# @time: 2022/7/4 11:32
# @author: Maxs_hu
"""
這裡用來收集ip
"""
from redis_proxy import RedisProxy
import requests
from lxml import html
from multiprocessing import Process
import time
import random


def get_kuai_ip(red):
    url = "https://free.kuaidaili.com/free/intr/"
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36"
    }
    resp = requests.get(url, headers=headers)
    etree = html.etree
    et = etree.HTML(resp.text)
    trs = et.xpath('//table//tr')
    for tr in trs:
        ip = tr.xpath('./td[1]/text()')
        port = tr.xpath('./td[2]/text()')
        if not ip:  # 將不含有ip值的篩除
            continue
        proxy_ip = ip[0] + ":" + port[0]
        red.add_ip(proxy_ip)


def get_unknown_ip(red):
    url = "https://ip.jiangxianli.com/"
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36"
    }
    resp = requests.get(url, headers=headers)
    etree = html.etree
    et = etree.HTML(resp.text)
    trs = et.xpath('//table//tr')
    for tr in trs:
        ip = tr.xpath('./td[1]/text()')
        port = tr.xpath('./td[2]/text()')
        if not ip:  # 將不含有ip值的篩除
            continue
        proxy_ip = ip[0] + ":" + port[0]
        red.add_ip(proxy_ip)


def get_happy_ip(red):
    page = random.randint(1, 5)
    url = f'http://www.kxdaili.com/dailiip/2/{page}.html'
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36"
    }
    resp = requests.get(url, headers=headers)
    etree = html.etree
    et = etree.HTML(resp.text)
    trs = et.xpath('//table//tr')
    for tr in trs:
        ip = tr.xpath('./td[1]/text()')
        port = tr.xpath('./td[2]/text()')
        if not ip:  # 將不含有ip值的篩除
            continue
        proxy_ip = ip[0] + ":" + port[0]
        red.add_ip(proxy_ip)


def get_nima_ip(red):
    url = 'http://www.nimadaili.com/'
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36"
    }
    resp = requests.get(url, headers=headers)
    etree = html.etree
    et = etree.HTML(resp.text)
    trs = et.xpath('//table//tr')
    for tr in trs:
        ip = tr.xpath('./td[1]/text()')  # 這裡存在空值. 所以不能在後面加[0]
        if not ip:
            continue
        red.add_ip(ip[0])


def get_89_ip(red):
    page = random.randint(1, 26)
    url = f'https://www.89ip.cn/index_{page}.html'
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36"
    }
    resp = requests.get(url, headers=headers)
    etree = html.etree
    et = etree.HTML(resp.text)
    trs = et.xpath('//table//tr')
    for tr in trs:
        ip = tr.xpath('./td[1]/text()')
        if not ip:
            continue
        red.add_ip(ip[0].strip())


def main():
    # 創建一個redis實例化對象
    red = RedisProxy()
    print("開始採集數據")
    while 1:
        try:
            # 這裡可以添加各種採集的網站
            print('>>>開始收集快代理ip')
            get_kuai_ip(red)  # 收集快代理
            # get_unknown_ip(red)  # 收集ip
            print(">>>開始收集開心代理ip")
            get_happy_ip(red)  # 收集開心代理
            print(">>>開始收集泥馬代理ip")
            # get_nima_ip(red)  # 收集泥馬代理
            print(">>>開始收集89代理ip")
            get_89_ip(red)
            time.sleep(60)
        except Exception as e:
            print('ip儲存出錯了', e)
            time.sleep(60)


if __name__ == '__main__':
    main()
    # 創建一個子進程
    # p = Process(target=main)
    # p.start()

ip_verify.py

# -*- encoding:utf-8 -*-
# @time: 2022/7/4 11:34
# @author: Maxs_hu
"""
這裡用來驗證ip的可用性: 使用攜程發送請求增加效率
"""
from redis_proxy import RedisProxy
from multiprocessing import Process
import asyncio
import aiohttp
import time


async def verify_ip(ip, red, sem):
    timeout = aiohttp.ClientTimeout(total=10)  # 設置網頁等待時間不超過十秒
    try:
        async with sem:
            async with aiohttp.ClientSession() as session:
                async with session.get(url='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('驗證沒有問題. 分值拉滿~', ip)
                    else:
                        # 如果不可用. 扣分
                        red.deduct_score(ip)
                        print('問題ip. 扣一分', ip)
    except Exception as e:
        print('出錯了', e)
        red.deduct_score(ip)
        print('問題ip. 扣一分', ip)


async def task(red):
    ips = red.get_all_proxy()
    sem = asyncio.Semaphore(30)  # 設置每次三十的信號量
    tasks = []
    for ip in ips:
        tasks.append(asyncio.create_task(verify_ip(ip, red, sem)))
    if tasks:
        await asyncio.wait(tasks)


def main():
    red = RedisProxy()
    time.sleep(5)  # 初始的等待時間. 等待採集到數據
    print("開始驗證可用性")
    while 1:
        try:
            asyncio.run(task(red))
            time.sleep(100)
        except Exception as e:
            print("ip_verify出錯了", e)
            time.sleep(100)


if __name__ == '__main__':
    main()
    # 創建一個子進程
    # p = Process(target=main())
    # p.start()

ip_api.py

# -*- encoding:utf-8 -*-
# @time: 2022/7/4 11:35
# @author: Maxs_hu

"""
這裡用來提供給用戶ip介面. 通過寫後臺伺服器. 用戶訪問我們的伺服器就可以得到可用的代理ip:
   1. flask
   2. sanic --> 今天使用這個要稍微簡單一點
"""
from redis_proxy import RedisProxy
from sanic import Sanic, json
from sanic_cors import CORS
from multiprocessing import Process

# 創建一個app
app = Sanic('ip')  # 隨便給個名字
# 解決跨域問題
CORS(app)
red = RedisProxy()


@app.route('maxs_hu_ip')  # 添加路由
def api(req):  # 第一個請求參數固定. 請求對象
   ip = red.effect_ip()
   return json({"ip": ip})


def main():
   # 讓sanic跑起來
   app.run(host='127.0.0.1', port=1234)


if __name__ == '__main__':
   main()
   # p = Process(target=main())
   # p.start()

runner.py

# -*- encoding:utf-8 -*-
# @time: 2022/7/5 17:36
# @author: Maxs_hu
from ip_api import main as api_run
from ip_collection import main as coll_run
from ip_verify import main as veri_run
from multiprocessing import Process


def main():
    # 設置互不幹擾的三個進程
    p1 = Process(target=api_run)  # 只需要將目標函數的記憶體地址傳過去即可
    p2 = Process(target=coll_run)
    p3 = Process(target=veri_run)

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


if __name__ == '__main__':
    main()

測試ip是否可用.py

# -*- encoding:utf-8 -*-
# @time: 2022/7/5 18:15
# @author: Maxs_hu
import requests


def get_proxy():
    url = "http://127.0.0.1:1234/maxs_hu_ip"
    resp = requests.get(url)
    return resp.json()


def main():
    url = 'http://mip.chinaz.com/?query=' + get_proxy()["ip"]
    proxies = {
        "http": 'http://' + get_proxy()["ip"],
        "https": 'http://' + get_proxy()["ip"]  # 目前代理只支持http請求
    }
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36",
    }
    resp = requests.get(url, proxies=proxies, headers=headers)
    resp.encoding = 'utf-8'
    print(resp.text)  # 物理位置


if __name__ == '__main__':
    main()

運行效果

項目運行截圖:

redis儲存截圖:

總結

  1. 免費代理ip只支持http的網頁操作. 並不好用. 如果有需求可以進行購買然後加入ip代理池
  2. 網頁部署到自己的伺服器上. 別人訪問自己的伺服器. 以後學了全棧可以加上登錄. 和付費功能. 實現功能的進一步拓展
  3. 項目架構是生產者消費者模型. 三個模塊同時運行. 每個模塊為一個進程. 互不影響
  4. 代理設計細節有待處理. 但總體運行效果還可以. 遇到問題再修改

本文來自博客園,作者:{Max},僅供學習和參考


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 黑夜模式 作為一個前端學習者,自然懂得黑夜模式的重要性,可惜主題原生未提供,那就自己弄吧 個人博客作為效果參考:https://jieniyou.github.io/ 設置基礎樣式 參考其他優秀產品的黑夜模式,得出共性: 那就是黑夜模式的背景一般不會是純黑(#000);而是淡黑色,字體也不是純白(# ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 先從面向對象講起,本瓜認為:面向對象編程,它的最大能力就是:復用! 咱常說,面向對象三大特點,封裝、繼承、多態。 這三個特點,以“繼承”為核心。封裝成類,是為了繼承,繼承之後再各自發展(重寫),可理解為多態。所以,根本目的是為了繼承,即“ ...
  • 這18個網站是我在取經路上意外發現的,裡面包括 純CSS 實現的炫酷背景,還有專門製作背景圖的網站。 算是取經路上的大補之物~ 1. CSS3 Patterns Gallery 🎗️ 傳送門:『CSS3 Patterns Gallery』 如果你認識 Lea Verou 的話,大概率知道這個網站, ...
  • 最近,在 CodePen 上看到這樣一個非常有意思的效果: 這個效果的核心難點在於氣泡的一種特殊融合效果。 其源代碼在:CodePen Demo -- Goey footer,作者主要使用的是 SVG 濾鏡完成的該效果,感興趣的可以戳源碼看看。 其中,要想靈活運用 SVG 中的 feGaussian ...
  • 1、樣式 1.1 行內樣式 <h1 style="color:red;">行內樣式</h1> 1.2 內部樣式 CSS代碼寫在 <head> 的 <style> 標簽中 <style> h1{color: green; } </style> 1.3 外部樣式 <link rel="styleshee ...
  • 本文簡介 你負責點贊,我負責更新~ 這次要用純CSS做一個波點背景,先上圖看看效果。 我把這個效果寫在 body 上,如果你不喜歡這個配色也可以自己手動改改。 思路 我實現上圖的效果思路是,最先想到使用 background-image ,然後使用 radial-gradient 畫圓。再配合預設給 ...
  • 講述在單片機軟體開發過程中如何更好地實現各個模塊的數據交互,降低耦合 ...
  • 恢復內容開始 選擇結構 選擇結構分為為i語句和switch語句 if語句 1.if單選擇語句 結構圖:(用輸出語句打了一個簡單結構圖) 代碼示例: public class IfDemo01 { public static void main(String[] args) { /*if單選擇語句 i ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...