Requests 是一个 Python 的一个第三方库,通过发送 HTTP 请求获取响应数据,一般应用于编写网络爬虫和接口测试等。

相比 urllib 库,它语法简单,更容易上手。

官方中文文档地址:Requests: 让 HTTP 服务人类

离线文档下载地址:Requests document download


安装 Reuqests

pip install requests

HTTP 简介

在使用 requests 模拟发送网络请求之前,先来简单学习一下HTTP和常见的请求方式。

什么是 HTTP

HTTP(HyperText Transfer Protocol ,超文本传输协议)是一个简单的请求/响应协议。即一个客户端与服务器建立连接后,向服务器发送一个请求;服务器接到请求后,给予相应的响应信息。

HTTP工作原理

客户端和服务器端之间的HTTP连接是一种一次性连接,它限制每次连接只处理一个请求,当服务器返回本次请求的应答后便立即关闭,下次请求再重新建立连接。这样做的好处就是让服务器不会处于一个一直等待的状态,及时释放连接可极大提高服务器的执行效率。

HTTP是一种无状态协议,意思就是服务器不保留与客户端连接时的任何状态。这减轻了服务器的记忆负担,从而保持较快的响应速度。

HTTP的9种请求方法

每种请求方式规定了客户端和服务器端之间不同的信息交换方式。

请求方法GET和POST的区别:

HTTP状态码

状态代码有三位数字组成,第一个数字定义了响应的类别,共分五种类别:

常见的状态码:

本节参考链接:

https://www.cnblogs.com/qdhxhz/p/8468913.html

https://blog.csdn.net/qq_40100414/article/details/120122782

如果你想学习关于 HTTP 的更多知识,可以关注公众号[愿泽君],输入"python requests"获取高清电子书和本文 markdown 笔记。


requests 快速上手

requests 发起请求的步骤

使用 requests 的流程大致可以分为以下三步:

graph LRid1(填写method url params等参数)-->id2(发起请求)-->id3(查看响应结果)

requests 发起请求的两种方式

使用 requests 发起请求有两种方式,以发起post请求为例:

import requests

# 方式一:
r = requests.request("post","https://www.baidu.com")
print(r.text)

# 方式二:
r = requests.post("https://www.baidu.com")
print(r.text)

requests.request(method, url, ...)的 request 是 requests 封装好根据 method 传参的不同而调用对应的请求方法。method 参数的值可以是 get/post/put/delete/head/patch/options 等,对应我们上一节的 HTTP 请求方法。上面的示例代码中方式一和方式二达到的效果都是一样的,但是推荐使用方式一,因为在后面的接口自动化测试中便于参数化,如下:

import requests

method = "get"
url = "https://www.baidu.com"

r = requests.request(method=method, url=url)
print(r.text)

请求参数

requests 发起请求时,支持传递的参数列表:

  • method:请求的类型,格式为字符串。值可以是 get\post\put\delete\files\head\patch\options
  • url:请求的接口地址,格式为字符串。此参数必传
  • params: get类型的接口请求的数据,格式为字典
  • data:form-data 一般用于 post 类型的接口请求的数据,格式为字典/json/字符串
  • json: json格式的参数,格式为字典
  • headers:请求头,格式为字典
  • cookies:格式为字典
  • files:上传文件,格式为字典
  • timeout:请求超时时间,float
  • allow_redirects:是否支持重定向,格式为boolean
  • verify:是否忽略http协议的证书错误,boolean:True 不忽略

在接下来的案例我们会逐一对上面的参数进行详细讲些。

发起 GET 请求

使用 Requests 模拟发送 GET 请求,以请求百度首页为例:

# 导入requests库
import requests

# 要请求的地址
url = "http://www.baidu.com"

# 发起 GET 请求,并将响应结果存储在 res 中,res是一个 responses 对象
res = requests.get(url)	

print(res.request.headers)		# 查看请求头信息
print(res.request.body)			# 查看请求正文
print(res.request.url)			# 查看请求url
print(res.request.method)		# 查看请求方法

print(res.content)		# 响应结果的字节码格式,一般用于图片,视频数据等
print(res.encoding)		# 查看响应正文的编码格式
print(res.text)			# 响应结果的字符串格式,非字节码
print(res.status_code)	# 响应结果状态码,200 表示成功
print(r.reason)			# 响应状态码的描述信息,如 OK,NotFound 等
print(res.cookies)		# 获取 cookies
print(res.headers)		# 查看响应的响应头
print(res.url)			# 查看响应的url

如果响应内容中文显示是乱码,在此提供2种解决方案:

import requests

url = "http://www.baidu.com"
res = requests.get(url)

# 方案1:
res.encoding="utf-8"	# 如果 res.text 中有中文乱码,修改编码格式为 "utf-8"
print(res.text)

# 方案2:
res.content.decode("utf-8")		# 将响应结果的字节码格式转换为 "utf-8" 格式
print(res.text)

1)发起携带参数的 GET 请求

来看一下 Request 中 get 方法的定义:

def get(url, params=None, **kwargs):
    return request("get", url, params=params, **kwargs)

这意味着发起 GET 请求时,允许我们使用 params 关键字参数,参数的类型为字典(dict)。接下来看一个案例:

慕课网(https://www.imooc.com/)首页搜索 "python",按 F12 --> 点击 NetWork 抓包获取其接口。

python requests 最牛攻略-LMLPHP

我们得到的接口部分信息如下:

请求方式:get
请求url:https://www.imooc.com/search/coursesearchconditions?words=python

?words=python问号后面的 word=python 就是我们在发起 get 请求时的要提供的参数,接下来使用 requests 来发起请求:

import requests

# 慕课网首页课程查询接口
url = "https://www.imooc.com/search/coursesearchconditions"

# 查询时携带的参数
payload = {
    'words': 'python'
}

res = requests.get(url, params=payload)	# 发起携带参数的 get 请求
print(res.json())	# 响应内容是 json 格式的字符串,我们使用 res.json() 方法进行解码

2)定制请求头

为什么要添加 UA?

在使用 Python 的 Requests 模拟浏览器向服务器发送 Http 请求时,于某些网站会设置对 User-Agent 反爬虫机制,因此我们发送 Http 请求时有必要的加上 User-Agent 来将爬虫程序的UA伪装成某一款浏览器的身份标识。

import requests

url = "https://www.imooc.com/search/coursesearchconditions"

headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36'}

# 查询时携带的参数
payload = {
    'words': 'python'
}

res = requests.get(url, params=payload, headers=headers)
print(res.json())	# 响应内容是 json 格式的字符串,我们使用 res.json() 方法进行解码
print(res.request.headers)  # 查看请求头

关于 Header 伪装策略的更多知识请参考以下博文:

发起 POST 请求

1)传递 data 参数

当我们要向网页上的一些表单(form)传递数据时,经常需要发起 post 请求。使用 requests 发起 post 请求的方法也非常简单,只需要传递一个字典给 data 参数。

import requests

url = 'http://httpbin.org/post'
payload = {'name': 'joy', 'phone': '400-7865-6666'}

r = requests.post(url=url, data=payload)
print(r.text)

运行结果:

{
  ...
  "form": {
    "key2": "value2",
    "key1": "value1"
  },
  ...
}

还可以为 data 参数传入一个元组列表。例如表单中多个元素使用同一个 key 时,可以像下面这样做:

import requests

url = 'http://httpbin.org/post'
payload = (('course', 'Python'), ('course', 'Java'))

r = requests.post(url=url, data=payload)
print(r.text)

响应结果:

{
  ...
  "form": {
    "course": [
      "Python",
      "Java"
    ]
  },
  ...
}

当你想用 data 参数去接收 json 格式的数据,那么需要把请求的数据转换成 json 格式,并且要将请求头设置为 application/json

import requests, json

url = 'https://api.github.com/some/endpoint'
data = json.dumps({
    "some": "data"
})
headers = {"Content-Type":"application/json"}

r = requests.post(url, data=data, headers=headers)
print(r.text)

2) 传递json参数

可以使用 json 参数直接传递,然后它就会被自动编码

import requests, json

url = "http://119.45.233.102:6677/testgoup/test/json"
data = {
    'name': 'jay',
    'age': 23
}
r = requests.post(url,json=data)
print(r.text)

这里科普一下 json 和 dict(字典)的区别:

3)传递 from-data 参数:
注意,requests默认是不支持from-data的请求数据的格式的。所以我们要传from-data格式,我们需要安装一个requests的插件:

pip install requests_toolbelt -i https://pypi.douban.com/simple
import requests
from requests_toolbelt.multipart.encoder import MultipartEncoder

method = "post"
url = "http://119.45.233.102:6677/testgoup/test/data"
data = MultipartEncoder({
    "name":"张三",
    "age":"23"
})
headers = {"Content-Type":data.content_type}
r = requests.request(method,url,data=data,headers=headers)
print(r.text)

4)传递 auth 参数

auth是一种对接口进行鉴权的方式,和cookies和token的作用差不多的。格式:元组,比如:(“账号”,“密码”)

import requests
url = "http://119.45.233.102:6677/testgoup/test/auth"
method  = "post"
auth = ("admin","123456")
r = requests.request(method,url,auth=auth)
print(r.text)

5)传递 timeout 参数

timeout用于控制响应的时间,如果超过了timeout规定的时间,那么会直接抛出连接失败的错误信息。timeout格式是整数,单位是秒。

import requests

method = "post"
url = "http://119.45.233.102:6677/testgoup/test/json"
data = {
    "name":"张三",
    "age":23
}
r = requests.request(method,url,json=data,timeout=10)
print(r.text)

6)传递 allow_redirects 参数

是否允许接口重定向。格式:布尔值

7)传递 proxies 参数

在编写爬虫程序时,同一个IP频繁对网站进行访问,可能会被封IP,为了避免这种情况我们就需要用到 proxies 参数来设置代理。proxies 参数可以将代理地址替换为你的IP地址,隐藏自身IP。
proxies 参数类型

proxies = { '协议':'协议://IP:端口号' }
proxies = {
    'http':'http://IP:端口号',
    'https':'https://IP:端口号',
}

可以去网上搜索免费的代理IP网站中查找免费代理IP(注意:如果获取到的免费IP地址无效就会报错):

# 使用免费普通代理IP访问测试网站: http://httpbin.org/get
import requests

url = 'http://httpbin.org/get'
headers = {'User-Agent':'Mozilla/5.0'}
# 定义代理,在代理IP网站中查找免费代理IP
proxies = {
    'http':'http://182.116.239.37:9999',
    'https':'https://182.116.239.37:9999'
}
html = requests.get(url,proxies=proxies,headers=headers,timeout=5).text
print(html)

这里推荐几个免费代理网站,可自行尝试:

8)传递 verify 参数
当我们请求https协议的接口的时候,如果它的证书过期了,我们就可以使用这个参数verify,设置为Fasle不检查证书,忽略证书的问题,继续请求。

requests 实战

登录接口的测试

TGU登录接口测试,登录接口信息如下:

地址:http://119.45.233.102:2244/testgoup/login
类型:post
请求头:application/json
请求参数:{
  "phone": "133********",
  "password": "e10adc3949ba59abbe56e057f20f883e",
  "type": 1
}
返回值:{
  "code": 1, 
  "data": {
    "nickName": "liuyanzu666", 
    "token": "eyJ..."
  }, 
  "message": "登录成功!"
}

使用requests测试登录接口:

import requests

loginUrl = 'http://119.45.233.102:2244/testgoup/login'
method='post'
data = {
  "phone": "133********",
  "password": "e10adc3949ba59abbe56e057f20f883e",
  "type": 1
}
r = requests.request(method=method, url=loginUrl, json=data)
print(r.text)

获取用户信息接口的测试

由于需要登录后才能获取到用户信息,在获取用户信息时需要传入登录后返回的token。完整代码如下:

import requests

loginUrl = 'http://119.45.233.102:2244/testgoup/login'
method='post'
data = {
  "phone": "133********",
  "password": "e10adc3949ba59abbe56e057f20f883e",
  "type": 1
}

#登录接口
r = requests.request(method=method, url=loginUrl, json=data)
# print(r.text)
token = r.json()['data']['token']

# 获取用户信息接口
userinfoUrl = 'http://119.45.233.102:2244/testgoup/user/getUserInfo'
headers = {'token': token}
r = requests.request(method='get', url=userinfoUrl, headers=headers)
print(r.text)

对响应结果的处理(序列化和反序列化)

上面代码中的 token = r.json()['data']['token'] 里有一个细节这里要展开叙述一下。来看一下登录接口返回的响应结果,也就是 print(r.text) 的值:

{
  "code": 1, 
  "data": {
    "nickName": "liuyanzu666", 
    "token": "eyJhbGci..."		# token太长了,这里删掉部分数据
  }, 
  "message": "登录成功!"
}

咋一看这是一个python字典类型的数据,有的同学可能说这是 json 类型的数据。到底是字典还是json类型的数据,我们使用 type() 方法对 r.text 进行判断即可。

print(type(r.text))		# 返回结果是 <class 'str'>

返回结果居然是 str 类型的数据。现在我们要从 r.text 中获取 token 值,如果我们将它从 str 类型转换成字典类型,那么就可以通过 token 键获取对应的 toekn 值了。这里就引出了我们要讲的知识点:

Python序列化和反序列化

序列化:将Python中字典类型的数据转换成json格式的字符串,以便进行存储和传输。

反序列化:将json格式的字符串转换成Python的字典类型数据,便于对其分析和处理。

我们可以使用 json 模块来实现序列化和反序列化:

import json

# 字典类型的数据
data = {
  "name": "张三",
  "age": 18
}

# 使用 json.dumps() 进行序列化:字典-->字符串
res = json.dumps(data)
print(res)  # 输出结果:{"name": "\u5f20\u4e09", "age": 18}
print(type(res))  # 输出结果:<class 'str'>

# 使用 json.loads() 进行反序列化:字符串-->字典
res2 = json.loads(res)
print(res2)   # 输出结果:{'name': '张三', 'age': 18}
print(type(res2)) # 输出结果:<class 'dict'>

上面阐述了使用 python 进行序列化和反序列化的方法,但是在登录接口中获取token值的时候 ,我们并没有使用 json.loads() 进行反序列化,而是使用 ``token = r.json()['data']['token'],也就是r.json()` 方法。

也就是说在上面的代码中,获取token我们可以使用两种方法:

...
r = requests.request(method=method, url=loginUrl, json=data)

方法一:
token = json.loads(r.text)['data']['token']

方法二:
token = r.json()['data']['token']

...	

至此,关于 Python Requests 的介绍就告一段落了,感谢您的阅读。如果本文对您有帮助,请帮我点个赞吧~如果想获取本文的 markdown 笔记、电子书和相关源代码,请关注我的WX公众号[愿泽君],发送 "python requests" 即可。

02-19 17:37