日常开发中,偶尔会遇到爬取网页数据的需求,为了隐藏本机真实 IP,常常会用到代理 IP 池,本文将基于 openresty 与代理 IP 池搭建更为易用的隧道代理。
代理 IP 池即在数据库中维护一个可用的 IP 代理队列,一般实现思路如下:
此处笔者采用的开源项目jhao104/proxy_pool,具体实现方式参考其文档。
import json
import requests
from retrying import retry
def get_proxy_ip() -> str:
resp = requests.get(url="http://192.168.0.121:5010/get")
assert resp.status_code == 200
return f"http://{json.loads(resp.text)['proxy']}"
@retry(stop_max_attempt_number=5)
def proxy_test() -> None:
resp = requests.get(url="http://httpbin.org/get", proxies={"http": get_proxy_ip()}, timeout=5)
assert resp.status_code == 200
print(f"origin: {json.loads(resp.text)['origin']}")
if __name__ == "__main__":
try:
proxy_test()
except Exception as e:
print(f"Error: {e}.")
通过代理 IP 池实现了隐藏本机真实 IP,但每次需要通过 API 接口获取新的代理 IP,不太方便,所以出现了隧道代理。隧道代理内部自动将请求通过不同的代理 IP 进行转发,对外提供统一的代理地址。
此处笔者通过 openresty 配合上文搭建的代理 IP 池实现隧道代理。
openresty
├── conf.d
│ └── tunnel-proxy.stream
├── docker.sh
└── nginx.conf
nginx.conf 文件为 openresty 的主配置文件,主要修改为引入了 stream 相关的配置文件,具体内容如下:
# nginx.conf -- docker-openresty
#
# This file is installed to:
# `/usr/local/openresty/nginx/conf/nginx.conf`
# and is the file loaded by nginx at startup,
# unless the user specifies otherwise.
#
# It tracks the upstream OpenResty's `nginx.conf`, but removes the `server`
# section and adds this directive:
# `include /etc/nginx/conf.d/*.conf;`
#
# The `docker-openresty` file `nginx.vh.default.conf` is copied to
# `/etc/nginx/conf.d/default.conf`. It contains the `server section
# of the upstream `nginx.conf`.
#
# See https://github.com/openresty/docker-openresty/blob/master/README.md#nginx-config-files
#
#user nobody;
#worker_processes 1;
# Enables the use of JIT for regular expressions to speed-up their processing.
pcre_jit on;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
# Enables or disables the use of underscores in client request header fields.
# When the use of underscores is disabled, request header fields whose names contain underscores are marked as invalid and become subject to the ignore_invalid_headers directive.
# underscores_in_headers off;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
# Log in JSON Format
# log_format nginxlog_json escape=json '{ "timestamp": "$time_iso8601", '
# '"remote_addr": "$remote_addr", '
# '"body_bytes_sent": $body_bytes_sent, '
# '"request_time": $request_time, '
# '"response_status": $status, '
# '"request": "$request", '
# '"request_method": "$request_method", '
# '"host": "$host",'
# '"upstream_addr": "$upstream_addr",'
# '"http_x_forwarded_for": "$http_x_forwarded_for",'
# '"http_referrer": "$http_referer", '
# '"http_user_agent": "$http_user_agent", '
# '"http_version": "$server_protocol", '
# '"nginx_access": true }';
# access_log /dev/stdout nginxlog_json;
# See Move default writable paths to a dedicated directory (#119)
# https://github.com/openresty/docker-openresty/issues/119
client_body_temp_path /var/run/openresty/nginx-client-body;
proxy_temp_path /var/run/openresty/nginx-proxy;
fastcgi_temp_path /var/run/openresty/nginx-fastcgi;
uwsgi_temp_path /var/run/openresty/nginx-uwsgi;
scgi_temp_path /var/run/openresty/nginx-scgi;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
# Don't reveal OpenResty version to clients.
# server_tokens off;
}
stream {
log_format proxy '$remote_addr [$time_local] '
'$protocol $status $bytes_sent $bytes_received '
'$session_time "$upstream_addr" '
'"$upstream_bytes_sent" "$upstream_bytes_received" "$upstream_connect_time"';
access_log /usr/local/openresty/nginx/logs/access.log proxy;
error_log /usr/local/openresty/nginx/logs/error.log notice;
open_log_file_cache off;
include /etc/nginx/conf.d/*.stream;
}
tunnel-proxy.stream 为配置隧道代理的文件,通过查询 Redis 获取代理 IP,并将请求通过代理 IP 转发到指定目标地址,具体内容如下:
# tunnel-proxy.stream
upstream backend {
server 0.0.0.0:9870;
balancer_by_lua_block {
local balancer = require "ngx.balancer"
local host = ngx.ctx.proxy_host
local port = ngx.ctx.proxy_port
local success, msg = balancer.set_current_peer(host, port)
if not success then
ngx.log(ngx.ERR, "Failed to set the peer. Error: ", msg, ".")
end
}
}
server {
# 对外代理监听端口
listen 9870;
listen [::]:9870;
proxy_connect_timeout 10s;
proxy_timeout 10s;
proxy_pass backend;
preread_by_lua_block {
local redis = require("resty.redis")
local redis_instance = redis:new()
redis_instance:set_timeout(3000)
# Redis 地址
local rhost = "192.168.0.121"
# Redis 端口
local rport = 6379
# Redis 数据库
local database = 0
# Redis Hash 键名
local rkey = "use_proxy"
local success, msg = redis_instance:connect(rhost, rport)
if not success then
ngx.log(ngx.ERR, "Failed to connect to redis. Error: ", msg, ".")
end
redis_instance:select(database)
local proxys, msg = redis_instance:hkeys(rkey)
if not proxys then
ngx.log(ngx.ERR, "Proxys num error. Error: ", msg, ".")
return redis_instance:close()
end
math.randomseed(tostring(ngx.now()):reverse():sub(1, 6))
local proxy = proxys[math.random(#proxys)]
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.NOTICE, "Proxy: ", proxy, ", ip: ", proxy_ip, ", port: ", proxy_port, ".");
ngx.ctx.proxy_host = proxy_ip
ngx.ctx.proxy_port = proxy_port
redis_instance:close()
}
}
通过 docker 启动 openresty,此处笔者为了方便,将 docker 命令保存成了 shell 文件,具体内容如下:
docker run --name openresty -itd --restart always \
-p 9870:9870 \
-v $PWD/nginx.conf:/usr/local/openresty/nginx/conf/nginx.conf \
-v $PWD/conf.d:/etc/nginx/conf.d \
-e LANG=C.UTF-8 \
-e TZ=Asia/Shanghai \
--log-driver json-file \
--log-opt max-size=1g \
--log-opt max-file=3 \
openresty/openresty:alpine
执行 bash docker.sh 命名启动 openresty,至此隧道代理搭建完成。
import json
import requests
from retrying import retry
proxies = {
"http": "http://192.168.0.121:9870"
}
@retry(stop_max_attempt_number=5)
def proxy_test() -> None:
resp = requests.get(
url="http://httpbin.org/get", proxies=proxies, timeout=5, )
assert resp.status_code == 200
print(f"origin: {json.loads(resp.text)['origin']}")
if __name__ == "__main__":
try:
proxy_test()
except Exception as e:
print(f"Error: {e}.")
参考链接:
我有一个存储主机名的Ruby数组server_names。如果我打印出来,它看起来像这样:["hostname.abc.com","hostname2.abc.com","hostname3.abc.com"]相当标准。我想要做的是获取这些服务器的IP(可能将它们存储在另一个变量中)。看起来IPSocket类可以做到这一点,但我不确定如何使用IPSocket类遍历它。如果它只是尝试像这样打印出IP:server_names.eachdo|name|IPSocket::getaddress(name)pnameend它提示我没有提供服务器名称。这是语法问题还是我没有正确使用类?输出:ge
我是Ruby的新手。我试过查看在线文档,但没有找到任何有效的方法。我想在以下HTTP请求botget_response()和get()中包含一个用户代理。有人可以指出我正确的方向吗?#PreliminarycheckthatProggitisupcheck=Net::HTTP.get_response(URI.parse(proggit_url))ifcheck.code!="200"puts"ErrorcontactingProggit"returnend#Attempttogetthejsonresponse=Net::HTTP.get(URI.parse(proggit_url)
有人知道如何将capybarapoltergeist的用户代理覆盖到移动用户代理以进行测试吗?我发现了一些有关为seleniumwebdriver配置它的信息:http://blog.plataformatec.com.br/2011/03/configuring-user-agents-with-capybara-selenium-webdriver/这在capybara闹鬼中怎么可能? 最佳答案 请参阅poltergeistgithub页面上的链接:https://github.com/teampoltergeist/polte
我正在使用Ruby/Mechanize编写一个“自动填写表格”应用程序。它几乎可以工作。我可以使用精彩CharlesWeb代理以查看服务器和我的Firefox浏览器之间的交换。现在我想使用Charles查看服务器和我的应用程序之间的交换。Charles在端口8888上代理。假设服务器位于https://my.host.com。.一件不起作用的事情是:@agent||=Mechanize.newdo|agent|agent.set_proxy("my.host.com",8888)end这会导致Net::HTTP::Persistent::Error:...lib/net/http/pe
我希望访问我机器上的所有HTTP流量(我的Windows机器-不是服务器)。据我了解,拥有一个本地代理是所有流量路线的必经之路。我一直在谷歌搜索但未能找到任何资源(关于Ruby)来帮助我。非常感谢任何提示或链接。 最佳答案 WEBrick中有一个HTTP代理(Rubystdlib的一部分)和here's一个实现示例。如果你喜欢生活在边缘,还有em-proxy伊利亚·格里戈里克。这postIlya暗示它似乎确实需要一些调整来解决您的问题。 关于ruby-如何捕获所有HTTP流量(本地代理)
我想在Ruby的TCPServer中获取客户端的IP地址。以及(如果可能的话)MAC地址。例如,Ruby中的时间服务器,请参阅评论。tcpserver=TCPServer.new("",80)iftcpserverputs"Listening"loopdosocket=tcpserver.acceptifsocketThread.newdoputs"Connectedfrom"+#HERE!HowcanigettheIPAddressfromtheclient?socket.write(Time.now.to_s)socket.closeendendendend非常感谢!
3月26日,映宇宙(HK:03700,即“映客”)发布截至2022年12月31日的2022年度业绩财务报告。财报显示,映宇宙2022年的总营收为63.19亿元,较2021年同期的91.76亿元下降31.1%。2022年,映宇宙的经营亏损为4698.7万元,2021年同期则为净利润4.57亿元;期内亏损(净亏损)为1.68亿元,2021年同期的净利润为4.33亿元;非国际财务报告准则经调整净利润为3.88亿元,2021年同期为4.82亿元,同比下降19.6%。 映宇宙在财报中表示,收入减少主要是由于行业竞争加剧,该集团对旗下产品采取更为谨慎的运营策略以应对市场变化。不过,映宇宙的毛利率则有所提升
我正在使用Net::FTPruby库连接到FTP服务器并下载文件。一切正常,但现在我需要使用出站代理,因为他们的防火墙将IP地址列入白名单,并且我正在使用Heroku来托管该站点。我正在试用新的Proximo附加组件,它看起来很有前途,但我无法让Net::FTP使用它。我在Net::FTPdocs中看到以下内容:connect(host,port=FTP_PORT)EstablishesanFTPconnectiontohost,optionallyoverridingthedefaultport.IftheenvironmentvariableSOCKS_SERVERisset,
我想使用nokogiri和mechanize自动化一个计时网络客户端。我需要通过代理服务器连接,但问题是,我不知道所述代理服务器的用户名和密码。我想获取存储在计算机上的此代理的缓存凭据..例如,在c#中你可以使用:stringproxyUri=proxy.GetProxy(requests.RequestUri).ToString();requests.UseDefaultCredentials=true;requests.Proxy=newWebProxy(proxyUri,false);requests.Proxy.Credentials=System.Net.Credential
Nginx在生产中的重要性通常基于它为慢速客户端提供服务的能力;在RESTfulAPI的设置中,它似乎是生产堆栈的一个不必要的层,尤其是Puma(不像广泛使用的unicorn可以处理nginx工作)。Pumacanallowmultipleslowclientstoconnectwithoutrequiringaworkertobeblockedontherequesttransaction.Becauseofthis,Pumahandlesslowclientsgracefully.HerokurecommendsPumaforuseinscenarioswhereyouexpect