Python web crawler(7)用requests模拟发送post请求

用requests模拟发送post请求

思考:哪些地方我们会用到POST请求?

  1. 登录注册( POST 比 GET 更安全)

  2. 需要传输大文本内容的时候( 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)
  1. requests.post(): 这是requests库中的一个函数,用于发送HTTP POST请求。

  2. url: 这是你想要发送POST请求的目标URL。

  3. headers=post_headers: 这是HTTP请求头的一个字典,它包含了额外的请求信息,如内容类型、认证信息等。在你提供的代码片段中,post_headers变量应该是一个包含HTTP请求头信息的字典。

  4. params=post_params: 这是用于添加查询字符串参数的字典。查询字符串是URL中?之后的部分,例如?key1=value1&key2=value2。在你提供的代码片段中,post_params变量应该是一个包含查询参数的字典。

  5. data=post_data: 这是你想要随POST请求一起发送的数据。它通常是一个字典、元组列表、字节或字符串。如果post_data是一个字典,requests库会将其转换为查询字符串。如果你想要发送JSON数据,你应该将字典转换为JSON字符串【执行post_data = json.dumps(post_data)】,并设置post_headers中的Content-Typeapplication/json

  6. json=post_data: 如果你确定发送的post_data是json格式文件,也可以直接写成json=post_data,你可以使用json参数直接传递Python对象,requests会自动将其序列化为JSON字符串并自动就设置了正确的Content-Type头。
  7. verify=False: 这个参数控制了是否应该验证SSL证书。

    1. 默认不写的情况下是verify=Truerequests会验证SSL证书以确保安全连接。

    2. 如果设置为Falserequests将不会验证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的流行也推动了其在其他领域的应用如:

  1. 网络数据交换:JSON 可以通过 HTTP 请求从服务器传输数据,适合于 Web 应用,例如传输用户数据、商品信息、文章内容等。JSON 是许多 API 接口所采用的数据格式,在设计 API 接口时,可以使用 JSON 格式规定请求参数和响应数据结构。
  2. 数据存储:JSON 可以将数据对象序列化为字符串,方便存储在数据库中或本地文件中,也可以将数据字符串反序列化为对象,方便操作。
  3. 配置文件:JSON 可以作为配置文件格式,存储应用的各种配置信息。
  4. 移动应用:很多移动应用使用 JSON 作为数据交换格式,通过 RESTful API 获取数据,展示页面信息。

这就抛出了主题:

在后现代web请求中,很多复杂数据的交互,所采用发送的格式“Content-Type: application/json”是json格式文件


1、先分析请求标头

先对请求头进行分析

  1. 这是一个post请求
  2. 可以看到完整的url请求后缀
  3. 发送的格式“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字典,反之亦然。

然而,它们之间有一些概念上的区别:

  1. 来源和用途
    • JSON(JavaScript Object Notation)是一种数据交换格式,用于在网络中传输数据,或者在不同的编程语言之间交换数据。
    • Python字典是Python语言内置的数据结构,用于存储键值对。
  2. 语法
    • JSON对象是由花括号 {} 包围的,内部包含零个或多个键值对,键值对之间用逗号 , 分隔。键和值之间用冒号 : 分隔。
    • Python字典也是由花括号 {} 包围的,内部同样包含零个或多个键值对,键值对之间用逗号 , 分隔。键和值之间用冒号 : 分隔。
  3. 键的类型
    • 在JSON中,所有的键都必须是字符串。
    • 在Python字典中,键可以是任何不可变类型,如字符串、数字或元组。
  4. 编码和解码
    • 当你要将Python字典发送到网络或保存到文件时,你需要使用json.dumps()将其编码(或序列化)为JSON格式的字符串。
    • 当你从网络接收或从文件读取JSON数据时,你需要使用json.loads()将其解码(或反序列化)为Python字典。

 

2、关于web编码转化的问题:

1、关于headersurldata不要进行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、表单数据等)进行正确的编码(如果需要的话)。(编码)
posted @ 2024-02-26 17:55  Magiclala  阅读(140)  评论(0编辑  收藏  举报