Python web crawler(7)用requests模拟发送post请求
用requests模拟发送post请求
思考:哪些地方我们会用到POST请求?
-
-
需要传输大文本内容的时候( POST 请求对数据长度没有要求)
所以同样的,我们的爬虫也需要在这两个地方回去模拟浏览器发送post请求
1.常用的post请求参数写法
import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning
url = 'XXXX'
post_headers = {
'Content-Type': 'XXXX',
'Cookie': 'xxxx',
'Referer': 'xxxx',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
}
post_params = {
'key1': 'value1',
'key2': 'value2',
}
post_data = {
"aaa": "xxxx",
"bbb": "xxxx",
"ccc": "xxxx",
}
# 关闭SSL证书警告(当verify=False可选)
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
# 将所有参数传入,发送post请求url
response = requests.post(url, headers=post_headers, params=post_params, data=post_data, verify=False)
# 如果是json文件请求,是可以直接使用json替换data参数
response = requests.post(url, headers=post_headers, params=post_params, json=post_data, verify=False)
-
requests.post()
: 这是requests
库中的一个函数,用于发送HTTP POST请求。 -
url
: 这是你想要发送POST请求的目标URL。 -
headers=post_headers
: 这是HTTP请求头的一个字典,它包含了额外的请求信息,如内容类型、认证信息等。在你提供的代码片段中,post_headers
变量应该是一个包含HTTP请求头信息的字典。 -
params=post_params
: 这是用于添加查询字符串参数的字典。查询字符串是URL中?
之后的部分,例如?key1=value1&key2=value2
。在你提供的代码片段中,post_params
变量应该是一个包含查询参数的字典。 -
data=post_data
: 这是你想要随POST请求一起发送的数据。它通常是一个字典、元组列表、字节或字符串。如果post_data
是一个字典,requests
库会将其转换为查询字符串。如果你想要发送JSON数据,你应该将字典转换为JSON字符串【执行post_data = json.dumps(post_data)
】,并设置post_headers
中的Content-Type
为application/json
。 json=post_data
: 如果你确定发送的post_data
是json格式文件,也可以直接写成json=post_data
,你可以使用json
参数直接传递Python对象,requests
会自动将其序列化为JSON字符串并自动就设置了正确的Content-Type
头。-
verify=False
: 这个参数控制了是否应该验证SSL证书。-
默认不写的情况下是
verify=True
,requests
会验证SSL证书以确保安全连接。 -
如果设置为
False
,requests
将不会验证SSL证书,这通常用于开发或测试环境中,但不应该在生产环境中使用,因为它会使连接容易受到中间人攻击。
-
2.发送简单的post请求,进行网站登录
import requests
# 请求头
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36',
'Referer': 'https://passport.17k.com/login/index.html'
}
# 携带post的表单数据
data = {
'loginName': '17323035232',
'password': 'sadxl1232232'
}
url = 'https://passport.17k.com/ck/user/login'
response = requests.post(url, headers=headers, data=data)
print(response.text)
3.发送复杂的post请求
这里要先讲述一下JSON文件
JSON(JavaScript Object Notation)最初的设计目的是为了替代Web开发中的XML(Extensible Markup Language)数据交换格式。XML虽然也是一种数据交换格式,但是它相对复杂、繁琐,并且在解析时需要额外的解析代码。相比之下,JSON格式更加简洁、易读、易写,并且与JavaScript的原生语法兼容,可以直接被JavaScript引擎解析和处理,无需额外的解析代码。因此,JSON逐渐成为了Web开发中广泛使用的数据交换格式,特别是在与服务器进行通信、获取数据等方面。
JSON的流行也推动了其在其他领域的应用如:
- 网络数据交换:JSON 可以通过 HTTP 请求从服务器传输数据,适合于 Web 应用,例如传输用户数据、商品信息、文章内容等。JSON 是许多 API 接口所采用的数据格式,在设计 API 接口时,可以使用 JSON 格式规定请求参数和响应数据结构。
- 数据存储:JSON 可以将数据对象序列化为字符串,方便存储在数据库中或本地文件中,也可以将数据字符串反序列化为对象,方便操作。
- 配置文件:JSON 可以作为配置文件格式,存储应用的各种配置信息。
- 移动应用:很多移动应用使用 JSON 作为数据交换格式,通过 RESTful API 获取数据,展示页面信息。
这就抛出了主题:
在后现代web请求中,很多复杂数据的交互,所采用发送的格式“Content-Type: application/json
”是json格式文件
1、先分析请求标头
先对请求头进行分析
- 这是一个post请求
- 可以看到完整的url请求后缀
- 发送的格式“Content-Type: application/json”是json格式文件
因此我们需要在"headers"中写上 'Content-Type': 'application/json',并添加其他几个必备的'Cookie'、'Referer'、'User-Agent'
headers = {
'Content-Type': 'application/json',
'Cookie': f'UEDC_LOGIN_POLICY_VALUE=checked; session_id={session_id}',
'Referer': f'https://172.18.145.20/index?id={id}',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
}
2、再分析载荷(payload)
查看“载荷”(payload)中的“查询字符串参数”和“请求载荷”,点击“查看源代码”
通过分析:“查询字符串参数”,确定需要添加的“params”参数
params = {
"id": 6ed181bc52334f8b8b0df3e33ab77b1b44ef,
}
通过分析:“请求载荷”,确定需要添加的“data”参数
data = {
"method": "GET",
"uri": f"/...rvice?%24select=health&id={id}",
"payload": ""
}
这里还需要注意一下,因为完整的"请求载荷"格式是:[{'aaa':'xxxx','bbb':'xxxx','ccc':'xxxx'}],这里必须注意最外层是有一层[ ]的(列表),也就是被转化了一次列表,因为data只是{'aaa':'xxxx','bbb':'xxxx','ccc':'xxxx'}
[{"method":"GET","uri":"/......rvice?%24select=health&id=cea446de2f3d6d82386d94b28a09f1f595cb","payload":""}]
我们知道,发送的格式“Content-Type: application/json”是json格式文件,{'aaa':'xxxx','bbb':'xxxx','ccc':'xxxx'},所以我们需要进行2次转化,先将data变成[data],然后再转化为json格式发送出去
data_json = json.dumps([data])
对于该段“请求载荷”正确的描述应该是:
payload
是一个JSON数组([]
)。- 这个数组只包含了一个JSON对象(
{}
),描述了另一个HTTP请求。 - 另一个GET请求跟咱们没有关系,是服务器与客户端约定好的固定格式,我们不用再深入去拆分了,我们只需要把这个外层的post请求处理好即可。
所以,当我们说“模拟这个请求”时,我们实际上是要发送一个POST请求,其中请求体(body)包含这个JSON数组,数组里有一个对象{描述了另一个GET请求的参数}。
PS:另外,你可以使用json
参数直接传递Python对象,requests
会自动将其序列化为JSON字符串并设置正确的Content-Type
头:
data = [{
"method": "GET",
"uri": f"/.....vice?$select=health&x_id={x_id}",
"payload": ""
}]
response = requests.post(url, headers=headers, params=params, json=data, verify=False)
以下是完整代码
import json
import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning
id = input('请输入id')
session_id = input('请输入session_id')
url = 'https://172.18.145.20/api/.../batch'
headers = {
'Content-Type': 'application/json',
'Cookie': f'UE.....UE=checked; session_id={session_id}',
'Referer': f'https://172.18.145.20/index?id={id}',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
}
params = {
"id": id,
}
data = {
"method": "GET",
"uri": f"/...service?$select=health&id={id}",
"payload": ""
}
# 将 webdata 字典转换为 JSON 字符串
data_json = json.dumps([data]) # 注意这里需要先将 data 放在一个列表中,再转化为JSON,因为 payload 是一个 JSON 数组
# 关闭SSL证书警告
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
# 设置 verify=False 来关闭 SSL 证书验证
response = requests.post(url, headers=headers, params=params, data=data_json, verify=False)
print(response.url)
print(response)
response.encoding = 'utf-8'
res_data = response.json() # 会直接返回字典 但是需要注意 如果返回数据中不是json数据,应该使用res_data = response.text 否则会报错
print(res_data)
补充:
1、关于JSON对象和Python的字典对比
在Python中,JSON对象和Python的字典是非常相似的。实际上,当你使用json
模块将JSON数据解码(即从字符串转换为Python对象)时,一个JSON对象会被转换成一个Python字典,反之亦然。
然而,它们之间有一些概念上的区别:
- 来源和用途:
- JSON(JavaScript Object Notation)是一种数据交换格式,用于在网络中传输数据,或者在不同的编程语言之间交换数据。
- Python字典是Python语言内置的数据结构,用于存储键值对。
- 语法:
- JSON对象是由花括号
{}
包围的,内部包含零个或多个键值对,键值对之间用逗号,
分隔。键和值之间用冒号:
分隔。 - Python字典也是由花括号
{}
包围的,内部同样包含零个或多个键值对,键值对之间用逗号,
分隔。键和值之间用冒号:
分隔。
- JSON对象是由花括号
- 键的类型:
- 在JSON中,所有的键都必须是字符串。
- 在Python字典中,键可以是任何不可变类型,如字符串、数字或元组。
- 编码和解码:
- 当你要将Python字典发送到网络或保存到文件时,你需要使用
json.dumps()
将其编码(或序列化)为JSON格式的字符串。 - 当你从网络接收或从文件读取JSON数据时,你需要使用
json.loads()
将其解码(或反序列化)为Python字典。
- 当你要将Python字典发送到网络或保存到文件时,你需要使用
2、关于web编码转化的问题:
1、关于headers
、url
和data
不要进行URL解码(应使用web编码后的数据,类似于%24
)
data
传参
data1和data2都可以吗
data1 = {
"method": "GET",
"uri": f"/a......service?%24select=health&x_id={x_id}",
"payload": ""
}
data2 = {
"method": "GET",
"uri": f"/a......service?$select=health&x_id={x_id}",
"payload": "" }
data1
和 data2
都是有效的字典,并且它们都可以用于模拟HTTP请求。在这两个字典中,唯一的区别是URI中的$
字符是否被编码为%24
。
在data1
中,URI使用了%24
来编码$
字符,这是URL编码的标准方式。当你发送一个请求时,服务器会自动将这个编码转换回原始的$
字符。因此,data1
是一个更加谨慎和通用的选择,因为它确保了URI的兼容性,即使在某些情况下服务器或代理服务器需要严格遵循URL编码规则。
在data2
中,URI直接使用了$
字符而没有进行编码。这在许多现代Web服务器和客户端库中都是有效的,因为它们通常能够正确处理未编码的$
字符。然而,为了确保最大的兼容性和避免潜在的问题,尤其是在处理复杂的网络请求或与其他系统集成时,使用编码后的版本(即data1
)通常是更好的选择。
总之,如果你不确定服务器或网络中的其他组件如何处理URI,那么使用编码后的%24
(如data1
所示)是更加安全和可靠的做法。如果你确定你的请求将仅被发送到能够处理未编码$
字符的服务器,那么使用data2
也是可以的。不过,为了保持代码的通用性和可维护性,建议始终使用编码后的版本。
当你在headers
字典中设置头部信息时,你不需要手动对值进行URL编码(也称为Web编码或百分比编码)。requests
库在发送请求时会自动处理必要的编码。
然而,你提供的'netns'
头部的值看起来已经是一个URL编码的字符串。如果这个值是你从DevTools中复制过来的,并且它原本就是服务器期望接收的格式,那么你应该保持它原样,不需要进行解码。
如果你尝试对这个值进行解码,它可能会变得混乱,因为服务器可能期望接收一个特定格式的URL编码字符串。
关于headers
传参:
当你在headers
字典中设置头部信息时,你不需要手动对值进行URL编码(也称为Web编码或百分比编码)。requests
库在发送请求时会自动处理必要的编码。
然而,你提供的'netns'
头部的值看起来已经是一个URL编码的字符串。如果这个值是你从DevTools中复制过来的,并且它原本就是服务器期望接收的格式,那么你应该保持它原样,不需要进行解码。
如果你尝试对这个值进行解码,它可能会变得混乱,因为服务器可能期望接收一个特定格式的URL编码字符串。
因此,你的headers
字典应该保留使用编码后的%24
字符串:
2、params
要恢复成最原始的情况(不能使用WEB编码后的数据)
在requests
库中,当你将参数作为字典传递给params
参数时,requests
会自动对这些键和值进行URL编码。这意味着你可以直接在字典中使用未编码的键,而不需要手动对它们进行编码。
从DevTools中看到的“载荷”中“源代码”是
all_properties=true&%24skip=0&%24top=100
在构建requests请求时,需要写成如下
params{
'all_properties':'true',
'$skip':'0',
'$top':'100'
}
在构建 requests
请求时,你不应该手动对查询参数进行URL编码。requests
库会自动为你处理查询参数的编码。如果你尝试手动编码查询参数,可能会导致双重编码或编码错误。
此时请求的url是正确的:
https://example.com/api/resource?all_properties=true&%24skip=0&%24top=100
如果你这样写:(错误写法)
params = {
'all_properties':'true',
'%24skip':'0',
'%24top':'100'
}
requests
会尝试对键和值都进行编码,导致 %24
(这实际上是URL编码后的 $
符号)被2次编码,这并不是你想要的结果。
此时请求的url是错误的:
https://example.com/api/resource?all_properties=true&%2524skip=0&%2524top=100
总结:
- 构建
params
时,直接传递未编码的键和值给params
字典,requests
会自动进行URL编码。为防止双重编码或编码错误,不要进行URL编码。(不编码) - 构建
url
时,确保URL已经是正确编码的,特别是如果URL中包含特殊符号、路径参数或查询参数。(编码) - 构建
headers
时,通常使用未编码的键值对,但如果有特殊字符需要按照HTTP规范进行编码,则手动进行编码。(编码) - 构建
data
时,根据数据类型(如JSON、表单数据等)进行正确的编码(如果需要的话)。(编码)