本次主要讲解请求头的顺序检测的反爬实现,
如下使用Django写的headers请求头顺序检验算法:
def index(request):
# 获取请求头的顺序
request_order = list(request.META.keys())
print(request_order)
# 定义标准请求头顺序
standard_order = [
"HTTP_HOST",
"HTTP_CONNECTION",
"HTTP_USER_AGENT",
"HTTP_ACCEPT_ENCODING",
"HTTP_ACCEPT_LANGUAGE"
]
# 检查标准请求头是否存在并按顺序排列
start_index = 0
for standard_header in standard_order:
try:
# 找到当前标准头的索引
current_index = request_order.index(standard_header, start_index)
# 更新起始索引,确保后续标准头在此索引之后
start_index = current_index + 1
except ValueError:
# 如果找不到当前标准头,返回失败
return HttpResponse("failed")
# 如果所有标准请求头都在正确的顺序中
return HttpResponse("success")
在浏览器端访问正常
在浏览器抓包复制 curl 生成python请求代码;
import requests
headers = {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"Accept-Language": "zh,zh-CN;q=0.9",
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"Pragma": "no-cache",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
}
url = "http://127.0.0.1:8080/index"
response = requests.get(url, headers=headers, verify=False)
print(response.text)
print(response)
这样请求会失败,然后试着按上面的后端逻辑改一下请求头
headers = {
'Host': '127.0.0.0.1:8080', # HTTP_HOST
'Connection': 'keep-alive', # HTTP_CONNECTION
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
# HTTP_USER_AGENT
'Accept-Encoding': 'gzip, deflate, br', # HTTP_ACCEPT_ENCODING
'Accept-Language': 'en-US,en;q=0.9', # HTTP_ACCEPT_LANGUAGE
}
发现还是失败的,在服务端打印请求日志发现实际上requests请求的时候请求头并没有按指定的顺序来,
在使用 requests
库时,确保请求头在发送时的顺序并非总能如预期那样实现,尤其是因为 HTTP 规范本身并不要求请求头的顺序。因此,尽管我们在代码中按照一定顺序添加头部,实际发送的 HTTP 请求可能并不遵循这个顺序。
解决方案
- 使用
http.client
进行更底层的控制:如果你必须确保请求头的顺序,http.client
或httpx
将是更好的选择。以下是使用http.client
的示例:
import http.client
# 目标主机
host = "127.0.0.1:8080"
# 创建 HTTP 连接
conn = http.client.HTTPConnection(host)
# 构造请求头,确保顺序
headers = {
'Host': '127.0.0.1:8080', # HTTP_HOST
'Connection': 'keep-alive', # HTTP_CONNECTION
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', # HTTP_USER_AGENT
'Accept-Encoding': 'gzip, deflate, br', # HTTP_ACCEPT_ENCODING
'Accept-Language': 'en-US,en;q=0.9' # HTTP_ACCEPT_LANGUAGE
}
# 发送请求
conn.request("GET", "/", headers=headers)
# 获取响应
response = conn.getresponse()
# 打印状态码和响应内容
print(response.status)
print(response.read().decode())
# 关闭连接
conn.close()
- 使用
httpx
:如果你希望使用更现代的库,httpx
是一个更好的选择,它支持异步请求和更强大的功能。
以下是使用httpx
的示例:
import httpx
url = 'http://127.0.0.1:8080/'
# 构造请求头,确保顺序
headers = [
('Host', '127.0.0.1:8080'), # HTTP_HOST
('Connection', 'keep-alive'), # HTTP_CONNECTION
('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'), # HTTP_USER_AGENT
('Accept-Encoding', 'gzip, deflate, br'), # HTTP_ACCEPT_ENCODING
('Accept-Language', 'en-US,en;q=0.9') # HTTP_ACCEPT_LANGUAGE
]
# 发送请求
response = httpx.get(url, headers=dict(headers))
# 打印状态码和响应内容
print(response.status_code)
print(response.text)
注意事项
-
HTTP 规范:HTTP 规范不保证请求头的顺序对于所有服务器都是重要的。大多数服务器会正确处理请求,即使请求头的顺序不同。
-
调试信息:如果你在请求时遇到问题,可以通过调试或查看服务器的日志信息来获取更多线索。
-
测试不同的库:如果请求仍然不如预期,可以尝试使用不同的库(如
http.client
或httpx
)以验证问题是否与requests
相关。