隧道转发代理: 简单来说每秒新建http请求,每个http请求随机分配一个代理IP,每个IP有效时间20秒。毕竟是随机分配的,每个http请求分配的代理IP都不一样,那如何保持每个http请求是同一个IP?
采用Proxy-Tunnel自主切换IP: 该模式适合一些需要登陆、Cookie缓存处理等爬虫需要精确控制IP切换时机的业务。 爬虫程序可以通过设置HTTP头Proxy-Tunnel: 随机数, 当随机数相同时,访问目标网站的代理IP相同。
例如 :需要登录,获取数据两个请求在一个IP下,只需对这组请求设置相同Proxy-Tunnel,例如:Proxy-Tunnel: 12345, 该组请求在代理有效期内使用相同的代理IP。
注意 :同一时间不同请求组可以设置不同Proxy-Tunnel: 随机数,并发完成数据爬取。
使用相同IP访问HTTPS目标网站:
1、使用Connection: keep-alive
和Proxy-Connection: keep-alive
方式访问目标网站,代理会确保在一个会话中的所有请求都通过一个IP到达目标网站。通过设置Proxy-Connection: Keep-Alive
或Connection: Keep-Alive
可以保持同一个Session代理IP不变。
2、设置相同Proxy-Tunnel
,有些库封装比较高层次,请务必确认向代理发送了该HTTP头。
例如:需要登录,获取数据两个请求在一个IP下,只需保证该组请求在一个T秒(Keep-Alive)会话下, 该组请求在代理有效期内使用相同的代理IP。
HTTPS 使用爬虫代理访问HTTPS网站时,会自动开启KeepAlive,同一个Session(HTTPS会话)代理IP不变。如需要每个请求强制切换IP,可以设置Proxy-Connection: Close
和Connection: Close
class ProxyMiddleware(object):
def process_request(self, request, spider):
# 代理服务器(产品官网 www.16yun.cn)
proxyHost = "t.16yun.cn"
proxyPort = "31111"
# 代理验证信息
proxyUser = "username"
proxyPass = "password"
request.meta['proxy'] = "http://{0}:{1}".format(proxyHost,proxyPort)
# 添加验证头
encoded_user_pass = base64ify(proxyUser + ":" + proxyPass)
request.headers['Proxy-Authorization'] = 'Basic ' + encoded_user_pass
# 设置IP切换头(根据需求)
tunnel = random.randint(1,10000)
request.headers['Proxy-Tunnel'] = str(tunnel)
session例,proxies为隧道代理
import requests
with requests.Session() as session:
url="http://icanhazip.com/"
response = session.get(url,timeout=20,proxies=proxies)
print('ip1:',response.text)
response = session.get(url,timeout=20,proxies=proxies)
print('ip2:',response.text)
需要了解,HTTP/1.1(以及 HTTP/1.0 的各种增强版本)允许 HTTP 在请求处理结束之后将 TCP 连接保持在打开状态(并不会立即关闭),以便为未来的 HTTP 请求重用之前建立的连接。
在请求处理结束之后仍然保持在打开状态的 TCP 连接被称为持久连接。而非持久连接则会在每个请求结束之后关闭。持久连接会在不同请求之间保持打开状态,直到客户端或服务端决定将其关闭为止。
重用已对目标服务器打开的空闲持久连接,就可以避开缓慢的连接建立阶段。而且,已经打开的连接还可以避免慢启动的拥塞适应阶段,以便更快速地进行数据的传输。
通过上面的内容我们了解到,开启Keep-Alive可以更快速的进行数据传输。
最常见的使用场景就是浏览器在打开一个网页,并不会为了每个资源都开打一个TCP连接,而是会同时打开少量TCP连接,利用Keep-Alive机制,不断利用少量连接传输多数HTTP请求。
由于去除了进行连接和关闭连接的开销,因此加快访问速度。
在一些编程语言开发的HTTP客户端,爬虫框架中,除了会使用异步方式发送请求,也会使用TCP连接复用来加速处理使用者的请求。
使用Requests提供的Session发送请求时,除了会自动保存cookie,还会使用urllib3底层提供的connection-pooling(连接池)
例如利用requests-session配置隧道代理动态版连续发送3个请求并打印出当前使用的代理IP
import time
import requests
username = "txxxxxxxxxxxxx"
password = "password"
tunnel = "tpsXXX.kdlapi.com"
proxies = {
"http": "http://%(user)s:%(pwd)s@%(proxy)s/" % {"user": username, "pwd": password, "proxy": tunnel},
"https": "http://%(user)s:%(pwd)s@%(proxy)s/" % {"user": username, "pwd": password, "proxy": tunnel}
}
s = requests.session()
for i in range(3):
res = s.get('https://dev.kdlapi.com/testproxy', proxies=proxies)
print(res.text)
time.sleep(1)
sucess! client ip: 175.7.196.238
sucess! client ip: 175.7.196.238
sucess! client ip: 175.7.196.238
可以发现三次都是同一个IP,并且使用Wireshark抓包查看,很明显这三次请求都走了一个TCP连接。
Scrapy底层采用的Twisted异步网络编程框架,在Twisted源码中就能找到对于连接复用的实现
twisted/web/_newclient.py
Class HTTP11ClientProtocol
_finishResponse_WAITING()
_finishResponse_WAITING
函数中判断响应header中的connection
是否为close
close
调用self._giveUp()
,直接关闭本次连接self.transport.resumeProducing()
,继续重用这条TCP连接,读取响应或者发送HTTP请求。由于隧道代理动态版底层的实现是**只有在新建立连接的情况下,才能将请求转发给不同的代理服务器。**如果使用代理的HTTP客户端在拿到响应后并没有直接关闭TCP连接,后续的HTTP请求可能会继续在这条TCP连接上发送,导致多个HTTP请求使用的是相同的代理IP。
那如何在请求结束后主动关闭连接呢?
在 HTTP/1.0 中,keep-alive 并 不 是 默 认 使 用 的。客 户 端 必 须 发 送 一 个Connection: Keep-Alive
请求首部来激活 keep-alive 连接。
而在HTTP/1.1中默认启用Keep-Alive, 默认情况下所在HTTP1.1中所有连接都被保持,除非在请求头或响应头中指明要在响应结束后关闭连接:Connection: Close
。
通常来说,只需要在请求头中加入Connection: Close
,目标服务器识别后,在响应头中也会加入Connection: Close
,并且在发送完响应后主动关闭连接。
所以如果你不能确定你所使用的HTTP客户端是否会在请求结束后关闭请求,是可以在发送请求的header中主动加上:Connection: Close
依旧是上述Python-Requests代码
# 使用隧道代理动态版发送请求
headers = {"Connection": "close"}
s = requests.session()
for i in range(3):
res = s.get('https://dev.kdlapi.com/testproxy', proxies=proxies, headers=headers)
print(res.text)
time.sleep(1)
sucess! client ip: 121.205.214.213
sucess! client ip: 27.148.203.221
sucess! client ip: 114.99.131.98
每次请求都更换了IP,再次查看Wireshark抓包数据,三次HTTP请求,每次都建立了新的TCP连接。
使用隧道代理动态版发现没有更换IP很有可能是HTTP客户端复用了之前建立的TCP连接,由此来加快网络请求。
大家购买隧道带动态版就是为了每次请求更换IP,复用了之前的TCP连接就无法达到更换IP的效果,只需要在请求头中加入Connection: Close
,显式地指出本次连接传输完成就立即关闭即可。
当然,如果您不需要每次请求都切换IP,使用keep_alive
机制可以加快你的请求,需要您根据业务实际情况进行判断。
参考:https://shequ.codemao.cn/community/352779 https://mp.weixin.qq.com/s/4sRmhtezyO0_ZnuW_H7SXA https://blog.csdn.net/weixin_44617651/article/details/132274278