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è) IP 代理隧道

        共 6573字,需瀏覽 14分鐘

         ·

        2021-07-31 02:25

        什么是隧道代理?我們來(lái)看下面這張截圖:

        10b21e85489d1ff663b3061d5e2be9b5.webp

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

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

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

        c2ca92a80b5539007a6dcae183b4e11e.webp

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

        52bed02e2edfdf357e42942e01eaf832.webp

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

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

        06a6bccf0778b7a7a3a4004eb32bf2b1.webp傳統(tǒng)代理服務(wù)5948288d2c5ae3d51754b158b7bdeb4d.webp隧道代理

        要自己開(kāi)發(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)求,頁(yè)面效果如下圖所示:

        3b0c6c5d6fe2e1fc3d722f38666cedf9.webp

        現(xiàn)在,你需要做的就是寫一個(gè)程序,周期性訪問(wèn)這個(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
        ~~~~~~~~~~~~~~~~~~~~~
        簡(jiǎn)易代理池管理工具,直接從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)起見(jiàn),我每40秒更換一次IP。更換的時(shí)候,采用了增量更換的方式。

        把當(dāng)前拉取的IP和Redis里面的已有IP進(jìn)行對(duì)比。不在這次拉取的IP全部從Redis移除,然后把新增的IP加到Redis中。

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

        實(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)。通過(guò)它,我們可以使用Lua語(yǔ)言實(shí)現(xiàn)一些邏輯,例如從Redis讀取數(shù)據(jù),把來(lái)源請(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來(lái)作為上游代理,并把剛剛發(fā)來(lái)的請(qǐng)求轉(zhuǎn)發(fā)到上游代理。從而實(shí)現(xiàn)隧道代理的效果。

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

        對(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;#?愛(ài)寫啥寫啥??反正下面的代碼也給你改了
        ????????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)

        ????????????--?如果沒(méi)有密碼,移除下面這一行
        ????????????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ī)的端口,也就是爬蟲(chóng)固定寫死的端口
        ???????listen?0.0.0.0:9976;?#監(jiān)聽(tīng)本機(jī)地址和端口,當(dāng)使用keeplived的情況下使用keeplived?VIP
        ???????proxy_connect_timeout?3s;
        ???????proxy_timeout?10s;
        ???????proxy_pass?backend;?#這里填寫對(duì)端的地址
        ????}
        }

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

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

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

        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趕快寫一段代碼來(lái)進(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)行效果如下圖所示。

        10b21e85489d1ff663b3061d5e2be9b5.webp

        說(shuō)明隧道代理搭建成功。目前隧道代理我已經(jīng)穩(wěn)定運(yùn)行了半年,從來(lái)沒(méi)有出過(guò)問(wèn)題,大家可以放心使用。

        最后,本文受到 @萌木蓋 的文章:openresty正向代理搭建 - 簡(jiǎn)書[2]的啟發(fā),并在該文章基礎(chǔ)上進(jìn)行改進(jìn)。特別感謝原作者。

        參考資料[1]

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

        [2]

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

        < END >爬蟲(chóng)|Js逆向某加速 cookie 加密分析爬蟲(chóng) | 找到了一個(gè)破解谷歌驗(yàn)證碼的新方案!
        瀏覽 83
        點(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>
            91黄色在线观看 | 男人捅女人在线观看 | 奶 啊 嗯高潮了嗯嗯嗯直播 | 日韩黄色片网站 | 黄色大片操逼 | 五月天激情综合网 | 国产裸体永久免费视频网站 | 免费国产一级黄色片 | 日本理伦三级做爰电影 | 国产第6页 |