1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        5分鐘,自己做一個(gè)隧道代理

        共 13570字,需瀏覽 28分鐘

         ·

        2021-07-28 03:23


        什么是隧道代理?我們來看下面這張截圖:

        所謂隧道代理,就是一個(gè)能幫你自動(dòng)更換代理 IP 的代理服務(wù)

        在你的代碼里面,你只需要把一個(gè)入口代理地址寫死,然后正常發(fā)起請(qǐng)求,而目標(biāo)服務(wù)器接收到的請(qǐng)求,每一次都是不同的代理地址

        在某代理網(wǎng)站上,隧道代理 50 并發(fā)每秒的價(jià)格是 4000 元/月:

        而常規(guī)的,先請(qǐng)求接口拿到一批代理 IP,再選一個(gè)發(fā)起請(qǐng)求的原始代理服務(wù)器,一個(gè)月價(jià)格才 600 多元:

        所以,如果我們能自己做一個(gè)隧道代理,將會(huì)省下很多錢!

        隧道代理的原理,跟常規(guī)代理的不同之處,用下面這兩張圖就能說清楚:

        傳統(tǒng)代理服務(wù)
        隧道代理

        要自己開發(fā)一個(gè)這樣的隧道代理,我們需要做兩步:

        1. 構(gòu)建一個(gè)代理池
        2. 實(shí)現(xiàn)代理自動(dòng)轉(zhuǎn)發(fā)

        構(gòu)建代理池

        假設(shè)你從代理供應(yīng)商手上買到的便宜代理地址為:http://xxx.com/ips,直接在瀏覽器上面請(qǐng)求,頁面效果如下圖所示:

        現(xiàn)在,你需要做的就是寫一個(gè)程序,周期性訪問這個(gè)url,拉取當(dāng)前最新可用的IP地址,然后把它放到Redis中。

        這里,我們使用 Redis 的Hash這個(gè)數(shù)據(jù)結(jié)構(gòu),其中Hash的字段名就是IP:端口,里面的值就是跟每個(gè)IP相關(guān)的一些信息。

        你這個(gè)程序需要確保,當(dāng)前在Redis里面的代理地址,全部都是可用的。這里,我給出了一個(gè)示例程序:

        """
        ProxyManager.py
        ~~~~~~~~~~~~~~~~~~~~~
        簡易代理池管理工具,直接從URL中讀取所有
        最新的代理,并寫入Redis。
        """

        import yaml
        import time
        import json
        import redis
        import datetime
        import requests


        class ProxyManager:
            def __init__(self):
                self.config = self.read_config()
                self.redis_config = self.config['redis']
                self.client = redis.Redis(host=self.redis_config['host'],
                                          password=self.redis_config['password'],
                                          port=self.redis_config['port'])
                self.instance_dict = {}

            def read_config(self):
                with open('config.yaml'as f:
                    config = yaml.safe_load(f.read())
                    return config

            def read_ip(self):
                resp = requests.get(self.config['proxy']).text
                if '{' in resp:
                    return []
                proxy_list = resp.split()
                return proxy_list

            def delete_ip(self, live_ips, pool_ips):
                ip_to_removed = set(pool_ips) - set(live_ips)
                if ip_to_removed:
                    print('ip to be removed:', ip_to_removed)
                    self.client.hdel(self.redis_config['key'], *list(ip_to_removed))

            def add_new_ips(self, live_ips, pool_ips):
                ip_to_add = set(live_ips) - set(pool_ips)
                if ip_to_add:
                    print('ip to add:', ip_to_add)
                    ips = {}
                    for ip in ip_to_add:
                        ips[ip] = json.dumps({'private_ip': ip,
                                              'ts': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')})
                    self.client.hset(self.redis_config['key'], mapping=ips)

            def run(self):
                while True:
                    live_ips = self.read_ip()
                    pool_ips = [x.decode() for x in self.client.hgetall(self.redis_config['key'])]
                    self.delete_ip(live_ips, pool_ips)
                    self.add_new_ips(live_ips, pool_ips)
                    time.sleep(40)


        if __name__ == '__main__':
            manager = ProxyManager()
            manager.run()

        其中,我把Redis相關(guān)的配置、代理供應(yīng)商的URL寫到了一個(gè)yaml配置文件中,防止被你們看到。配置文件的格式如下圖所示:

        由于我這個(gè)代理供應(yīng)商提供的IP,有效期是1-5分鐘,所以保險(xiǎn)起見,我每40秒更換一次IP。更換的時(shí)候,采用了增量更換的方式。把當(dāng)前拉取的IP和Redis里面的已有IP進(jìn)行對(duì)比。不在這次拉取的IP全部從Redis移除,然后把新增的IP加到Redis中。

        大家在實(shí)際過程中,還可以加一些代理校驗(yàn)的邏輯,確保從URL拉下來的代理也進(jìn)行有效性檢查,發(fā)現(xiàn)無效的立刻移除。

        實(shí)現(xiàn)自動(dòng)轉(zhuǎn)發(fā)

        要實(shí)現(xiàn)自動(dòng)轉(zhuǎn)發(fā),我們可以使用OpenResty[1]

        它是一個(gè)基于Nginx和Lua實(shí)現(xiàn)的高性能Web平臺(tái)。通過它,我們可以使用Lua語言實(shí)現(xiàn)一些邏輯,例如從Redis讀取數(shù)據(jù),把來源請(qǐng)求轉(zhuǎn)發(fā)到上游代理服務(wù)器……

        因此,我們使用OpenResty搭建一個(gè)轉(zhuǎn)發(fā)服務(wù)。并把這個(gè)轉(zhuǎn)發(fā)服務(wù)所在服務(wù)器的IP地址作為我們的入口IP地址。在使用Requests等等網(wǎng)絡(luò)請(qǐng)求客戶端發(fā)送請(qǐng)求的時(shí)候,只需要把這個(gè)入口IP地址設(shè)置為代理。那么,當(dāng)客戶端發(fā)送請(qǐng)求的時(shí)候,請(qǐng)求首先到了OpenResty。然后它從Redis中隨機(jī)選一個(gè)代理IP來作為上游代理,并把剛剛發(fā)來的請(qǐng)求轉(zhuǎn)發(fā)到上游代理。從而實(shí)現(xiàn)隧道代理的效果。

        Lua是一門非常老的語言,它的語法不少地方跟Python不太一樣。不過你不用擔(dān)心,這個(gè)配置文件我已經(jīng)寫好了。大家拿過來改一改就能用。

        對(duì)應(yīng)的配置文件如下圖所示:

        worker_processes  16;        #nginx worker 數(shù)量
        error_log /usr/local/openresty/nginx/logs/error.log;   #指定錯(cuò)誤日志文件路徑
        events {
            worker_connections 1024;
        }


        stream {
            ## TCP 代理日志格式定義
            log_format tcp_proxy '$remote_addr [$time_local] '
                                 '$protocol $status $bytes_sent $bytes_received '
                                 '$session_time "$upstream_addr" '
                                 '"$upstream_bytes_sent" "$upstream_bytes_received" "$upstream_connect_time"';
            ## TCP 代理日志配置
            access_log /usr/local/openresty/nginx/logs/access.log tcp_proxy;
            open_log_file_cache off;

            ## TCP 代理配置
            upstream backend{
                server 127.0.0.2:1101;# 愛寫啥寫啥  反正下面的代碼也給你改了
                balancer_by_lua_block {
                    -- 初始化balancer
                    local balancer = require "ngx.balancer"
                    local host = ""
                    local port = 0
                    host = ngx.ctx.proxy_host
                    port = ngx.ctx.proxy_port
                    -- 設(shè)置 balancer
                    local ok, err = balancer.set_current_peer(host, port)
                    if not ok then
                        ngx.log(ngx.ERR, "failed to set the peer: ", err)
                    end
                }
            }


            server {
                preread_by_lua_block{

                    local redis = require("resty.redis")
                    --創(chuàng)建實(shí)例
                    local redis_instance = redis:new()
                    --設(shè)置超時(shí)(毫秒)
                    redis_instance:set_timeout(3000)
                    --建立連接,請(qǐng)?jiān)谶@里配置Redis 的 IP 地址、端口號(hào)、密碼和用到的 Key
                    local rhost = "123.45.67.89"
                    local rport = 6739
                    local rpass = "abcdefg"
                    local rkey = "proxy:pool"
                    local ok, err = redis_instance:connect(rhost, rport)
                    ngx.log(ngx.ERR, "1111111 ", ok, " ", err)

                    -- 如果沒有密碼,移除下面這一行
                    local res, err = redis_instance:auth(rpass)
                    local res, err = redis_instance:hkeys(rkey)
                    if not res then
                        ngx.log(ngx.ERR,"res num error : ", err)
                        return redis_instance:close()
                    end
                    math.randomseed(tostring(ngx.now()):reverse():sub(1, 6))
                    local proxy = res[math.random(#res)]
                    local colon_index = string.find(proxy, ":")
                    local proxy_ip = string.sub(proxy, 1, colon_index - 1)
                    local proxy_port = string.sub(proxy, colon_index + 1)
                    ngx.log(ngx.ERR,"redis data = ", proxy_ip, ":", proxy_port);
                    ngx.ctx.proxy_host = proxy_ip
                    ngx.ctx.proxy_port = proxy_port
                    redis_instance:close()
                }
                #  下面是本機(jī)的端口,也就是爬蟲固定寫死的端口
               listen 0.0.0.0:9976; #監(jiān)聽本機(jī)地址和端口,當(dāng)使用keeplived的情況下使用keeplived VIP
               proxy_connect_timeout 3s;
               proxy_timeout 10s;
               proxy_pass backend; #這里填寫對(duì)端的地址
            }
        }

        需要修改的地方,我在配置文件里面已經(jīng)做好的注釋。具體而言,需要修改地方包含:

        • Redis的地址、端口、密碼和 Key。如果你的Redis沒有密碼,可以把設(shè)置密碼的這一行刪掉
        • 入口代理的端口

        設(shè)置好了這些配置以后,我們就可以使用Docker來啟動(dòng)它。Docker的配置文件極其簡單:

        from openresty/openresty:centos

        copy nginx_redis.conf /usr/local/openresty/nginx/conf/nginx.conf

        然后,執(zhí)行命令構(gòu)建和運(yùn)行:

        docker build --network host -t tunnel_proxy:0.01 .
        docker run --name tunnel_proxy --network host -it tunnel_proxy:0.01

        運(yùn)行以后,你會(huì)看到Docker的命令行似乎卡住了。這是正常請(qǐng)求。因?yàn)樾枰阌辛苏?qǐng)求,它才會(huì)輸出內(nèi)容。

        現(xiàn)在,你可以用Requests趕快寫一段代碼來進(jìn)行驗(yàn)證:

        import requests
        import time

        proxies = {'http''http://13.88.220.207:9976'}
        for _ in range(10):
            resp = requests.get('http://httpbin.org/ip', proxies=proxies).text
            print(resp)
            time.sleep(1)

        運(yùn)行效果如下圖所示:

        說明隧道代理搭建成功

        目前隧道代理我已經(jīng)穩(wěn)定運(yùn)行了半年,從來沒有出過問題,大家可以放心使用

        參考資料

        [1]

        OpenResty: https://openresty.org/cn/

        [2]

        openresty正向代理搭建 - 簡書: https://www.jianshu.com/p/7808ee6395ab


        瀏覽 96
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            引诱女乱h怀孕 | www.天天日.com | 欧美三级在线观看视频 | 女人扒开屁股让我添 | 挺进邻居少妇雪白身体视频 | 91人妻人人爽人人爽 | 偷拍各种男澡堂网站 | 欧美黄片免费看 | chinamodel私拍大尺度 | 日本亚洲精品中字幕日产2020 |