基於後端和爬蟲創建的代理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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...