Requests从入门到进阶

本文详细介绍了Python的Requests库,包括其特点、安装、发送GET和POST请求、设置请求头、Cookies、JSON与XML数据的发送、文件上传、SSL证书验证、代理设置、超时处理、会话管理等。内容涵盖各种请求参数的使用方法和注意事项,适合Python网络请求初学者和进阶者阅读。

特点

  • Keep-Alive & 连接池
  • 国际化域名和 URL
  • 带持久 Cookie 的会话
  • 浏览器式的SSL认证
  • 自动内容解码
  • 基本/摘要式的身份认证
  • 优雅的key/value Cookie
  • 自动解压
  • Unicode 响应体
  • HTTP(S) 代理支持
  • 文件分块上传
  • 流下载
  • 连接超时
  • 分块请求
  • 支持 .netrc

缺点:

  • 同步阻塞模式,不支持异步和协程
  • 尚不支持HTTP2.0

官方文档:https://requests.readthedocs.io/zh_CN/latest/

安装

通过pip命令安装即可:pip install requests

发送请求

发送GET请求

使用requests发送请求,只要使用request.get(url)方法填入对应的接口地址即可,支持携带URL参数。调用方法返回响应对象,可以通过响应对象的status_code、text、headers等属性,来获取状态码、响应文本和响应头等数据,示例如下。

import requests
res = requests.get('https://httpbin.org/get?name=临渊&age=18')
print('状态码', res.status_code)
print('响应文本', res.text)
print('响应头', res.headers)

URL只支持ASCII(美国标准码),在实际的传输过程中,中文及一些特殊字符需要经过urlencode(URL编码)。如上例中的接口地址会被编码成:

https://httpbin.org/get?name=%E4%B8%B4%E6%B8%8A&age=18

requests在发送请求时会自动进行编码,运行后显示如下。

状态码 200
响应文本 {
  "args": {
    "age": "18", 
    "name": "\u4e34\u6e0a"
  }, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.18.4"
  }, 
  "origin": "111.194.126.253, 111.194.126.253", 
  "url": "https://httpbin.org/get?name=\u4e34\u6e0a&age=18"
}

响应头 {'Access-Control-Allow-Credentials': 'true', 'Access-Control-Allow-Origin': '*', 'Content-Encoding': 'gzip', 'Content-Type': 'application/json', 'Date': 'Mon, 20 Jan 2020 02:33:47 GMT', 'Referrer-Policy': 'no-referrer-when-downgrade', 'Server': 'nginx', 'X-Content-Type-Options': 'nosniff', 'X-Frame-Options': 'DENY', 'X-XSS-Protection': '1; mode=block', 'Content-Length': '222', 'Connection': 'keep-alive'}

使用Params

Params又叫Query Params,即URL参数,如?name=临渊&age=18。如果参数很多,直接写到URL中会比较长,不方便查看和修改。URL参数由多组键值对组成。可以通过字典传给requests请求放到的params参数,即request.get(url, params={}),示例如下。

import requests
res = requests.get('https://httpbin.org/get', params={
   
   'name': '临渊', 'age': '18'})
print('响应文本转为字典', res.json())

由于参数可能较多,一般我们可以使用变量,先把url及参数等数据组装好,然后在传入请求方法中。

res.json()方法实际上是使用了json.loads(res.text)将响应文本尝试以JSON格式转为字典。由于该方法存在异常(比如正常情况下返回JSON格式,500报错时则会返回非JSON格式的报错信息),建议使用try…except处理,修改如下。

import requests
url = 'https://httpbin.org/get'
url_params = {
   
   'name': '临渊', 'age': '18'}
res = requests.get(url, params=url_params)
try:
    print('响应文本转为字典', res.json())
except:
    print('响应文本', res.text)

url_params是自定义的变量名,一般笔者习惯使用params作为变量名,来表示和请求方法参数params的对应关系。即

params = {
   
   'name': '临渊', 'age': '18'}
res = requests.get(url, params=params)

运行结果如下。

响应文本转为字典 {
   
   'args': {
   
   'age': '18', 'name': '临渊'}, 'headers': {
   
   'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.18.4'}, 'origin': '111.194.126.253, 111.194.126.253', 'url': 'https://httpbin.org/get?name=临渊&age=18'}

使用请求头

请求头是链接和请求数据的一些辅助说明信息,常见的请求头有:

  • Accept:客户端能接受的内容类型
  • Accept-Charset:浏览器可以接受的字符编码
  • Accept-Encoding:浏览器可以支持的压缩编码类型
  • Accept-Languge:浏览器可以接受的语言
  • Referer:连接来路
  • User-Agent:发送请求的客户端信息
  • Connection:连接类型(Keepalive保持连接/Close关闭连接)
  • X-Requested-With:XMLHttpRequest(是Ajax异步请求)
  • Cookie:服务器标记信息
  • Cache-Control:缓存机制(no-cache无缓存或max-age=缓存保存时间)
  • Expries:缓存过期时间
  • Content-Type:内容类型(MIME类型)
  • Content-Length:数据长度

请求头项一般不区分大小写。Cookie是请求头的一项(注意为单数形式,不带s)。因此在请求一些需要登录状态的接口时可以手动抓取到Cookie,放到请求头中使用,示例如下。
(1)手动登录后,通过Chrome开发者工具抓取请求正常访问时的请求头信息。

请求头中一般cookie用于验证登录,referer用于防止盗链,user-agent用于反爬。

(2)组装字典格式的请求头并使用
请求头一般有一组组键值对组成,我们同样使用Python中的的字典格式,构造出请求头数据,并传递给请求方法的headers参数即可。

import requests
url = 'https://www.jianshu.com/shakespeare/v2/notes/9d3f991c901a/book'
headers = {
   
   
    'user-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36',
    'referer': 'https://www.jianshu.com/p/9d3f991c901a',
    'cookie': '__yadk_uid=ratDswBmN3Kzid42v2gKV2q8veUvOsEd; read_mode=day; default_font=font2; locale=zh-CN; remember_user_token=W1s3NTc1NzIxXSwiJDJhJDExJFRVVTNvMlV6NjJaVTlXZjF0YWFuZi4iLCIxNTc5NTczNDg1Ljk2MzgyODYiXQ%3D%3D--feb4c1d88427a88d7321791daf2d76f7b11ed4b3; _m7e_session_core=d0065296a1834086d0279a548d932927; Hm_lvt_0c0e9d9b1e7d617b3e6842e85b9fb068=1579573487,1579587460,1579591333,1579591335; Hm_lpvt_0c0e9d9b1e7d617b3e6842e85b9fb068=1579601795; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%221697595f5777a9-0a3caa4a8cc382-36667905-1024000-1697595f578430%22%2C%22%24device_id%22%3A%221697595f5777a9-0a3caa4a8cc382-36667905-1024000-1697595f578430%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E7%9B%B4%E6%8E%A5%E6%B5%81%E9%87%8F%22%2C%22%24latest_referrer%22%3A%22%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC_%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%22%2C%22%24latest_utm_source%22%3A%22weixin-friends%22%2C%22%24latest_utm_medium%22%3A%22reader_share%22%2C%22%24latest_utm_campaign%22%3A%22hugo%22%2C%22%24latest_utm_content%22%3A%22note%22%7D%2C%22first_id%22%3A%22%22%7D'
}
res = requests.get(url, headers=headers)
print(res.text)

headers=headders第一个headers是请求方法的固定参数,第二个headers是我们自定义的字典变量(变量也可以使用其他名称),执行后打印信息如下。

{
   
   "notebook_id":26739010,"notebook_name":"Python接口测试","liked_by_user":false}

注:本例中请求头实际并没有登录限制,只需要在请求头添加了user-agent即可正常使用。

使用Cookies

Cookies可以作为一个整体的字符串放到请求头的Cookie字段中,当Cookies很多并且需要组装时,使用字符串会比较长并难以维护。此时可以将Cookies拆开成一组组键值对,构造为字典格式的数据,传递给请求方法的cookies参数,示例如下。

import requests
url = 'https://www.jianshu.com/shakespeare/v2/notes/9d3f991c901a/book'
headers = {
   
   
    'user-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36',
    'referer': 'https://www.jianshu.com/p/9d3f991c901a',
}
cookies = {
   
   'Hm_lpvt_0c0e9d9b1e7d617b3e6842e85b9fb068': '1579601795',
 'Hm_lvt_0c0e9d9b1e7d617b3e6842e85b9fb068': '1579573487,1579587460,1579591333,1579591335',
 '__yadk_uid': 'ratDswBmN3Kzid42v2gKV2q8veUvOsEd',
 '_m7e_session_core': 'd0065296a1834086d0279a548d932927',
 'default_font': 'font2',
 'locale': 'zh-CN',
 'read_mode': 'day',
 'remember_user_token': 'W1s3NTc1NzIxXSwiJDJhJDExJFRVVTNvMlV6NjJaVTlXZjF0YWFuZi4iLCIxNTc5NTczNDg1Ljk2MzgyODYiXQ%3D%3D--feb4c1d88427a88d7321791daf2d76f7b11ed4b3',
 'sensorsdata2015jssdkcross': '%7B%22distinct_id%22%3A%221697595f5777a9-0a3caa4a8cc382-36667905-1024000-1697595f578430%22%2C%22%24device_id%22%3A%221697595f5777a9-0a3caa4a8cc382-36667905-1024000-1697595f578430%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E7%9B%B4%E6%8E%A5%E6%B5%81%E9%87%8F%22%2C%22%24latest_referrer%22%3A%22%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC_%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%22%2C%22%24latest_utm_source%22%3A%22weixin-friends%22%2C%22%24latest_utm_medium%22%3A%22reader_share%22%2C%22%24latest_utm_campaign%22%3A%22hugo%22%2C%22%24latest_utm_content%22%3A%22note%22%7D%2C%22first_id%22%3A%22%22%7D'
}

res = requests.get(url, headers=headers, cookies=cookies)
print(res.text)

注:Cookies中不能拥有非ASCII字符,中文应进行URL编码后使用。

同名参数处理:
假设url中具有同名参数,如name=临渊,age=18,age=30。由于字典中不能存在同名的键,我们可以使用嵌套列表实现。示例如下。
params = [('name','临渊'), ('age', '18'), ('age', '30')]
请求方法中的其他参数,如data、headers等,如果存在同名变量也可以这样处理。

发送POST请求

POST方法和GET方法本质上一样的,都是HTTP请求的一种请求动作。只是通常情况下GET请求不使用请求体数据,而POST使用。既然POST方法会发送请求体数据,就会涉及到数据类型的问题。客户端和服务端商量好,才能正常的解析和通讯。这种数据类型又称为媒体类型,标准称法为MIME(Multipurpose Internet Mail Extensions)类型,即多用途互联网邮件扩展类型。数据类型的声明,一般放在请求头(请求辅助信息)的Content-Type字段中,常见的有以下几种格式。

  • application/x-www-form-url-encoded:表单URL编码格式
  • multipart/form-data:复合表单格式(支持文件上传,文件二进制
  • application/json:JSON格式
  • application/xml:XML格式

不同数据类型的请求,数据组装方式也不同,至于什么时候用表单,什么时候用JSON格式要看接口文档或问开发小哥哥,接口在编写时便已确定好了需要使用的的数据(媒体)类型。

发送POST请求使用requests的post方法即可,格式如下。

res = requests.post(url,data={
   
   }, json={
   
   }, files={
   
   })

data、json、files都是可选参数(一般同时只用其中一个)。分别用来将数据按不同格式编码发送。

  • data参数接受字典时将数据按普通表单(application/x-www-form-url-encoded)格式发送。
  • json参数存在时将字典格式的请求数据按JSON格式(application/json)发送
  • files参数将字典格式的请求数据(可以包含打开的文件)按混合表单(multipart/form-data)格式发送。

同时使用三者之一时,会自动在请求头中添加对应的内容类型声明Content-Type:...

当data参数接受字符串格式的参数是按Raw原始格式发送,不进行编码和添加请求头。当data参数接受文件对象时按binary二进制格式发送。

发送FORM表单格式数据

Form表单指网页中包含输入框、选择框、按钮等组成的一组用户填写及选择的数据。如登录、注册表单。表单是最常用的一种请求数据类型,对应的请求头媒体类型声明:Content-Type:application/x-www-form-urlencoded

之所以称为urlencoded,是因为,请求体数据,实际会按url编码格式发送,如name=临渊,password=123456实际上会编码为
name=%E4%B8%B4%E6%B8%8A&password=123456作为请求体数据,后台传输。

表单类型的参数同样是由多组键值对组成,我们同样适用字典格式构造请求体数据并传递给请求方法的data参数即可,示例如下。

import requests
url = 'https://httpbin.org/post'
data = {
   
   'name': '临渊', 'password': '123456'}
res = requests.post(url, data=data)
print(res.text)

发送POST请求只要使用requests.post()方法即可,方法中的data=data,第一个data是请求方法的一个固定的关键字参数,后面的data是上面我自定义的变量,即{'name': '临渊', 'password': '123456'},使用其他变量名可以。打印结果如下。

{
   
   
  "args": {
   
   }, 
  "data": "", 
  "files": {
   
   }, 
  "form": {
   
   
    "name": "\u4e34\u6e0a", 
    "password": "123456"
  }, 
  "headers": {
   
   
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "39", 
    "Content-Type": "application/x-www-form-urlencoded", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.18.4"
  }, 
  "json": null, 
  "origin": "111.194.126.253, 111.194.126.253", 
  "url": "https://httpbin.org/post"
}

发送时,请求头中会自动添加"Content-Type": "application/x-www-form-urlencoded"
对于JSON格式的响应数据,我们可以使用res.json()转为字典格式并通过字典取值提取响应字段的变量进行断言。假设我们要断言响应结果的url为"https://httpbin.org/post",form不为空且name和password是我们传的值,示例如下。

res_dict = res.json()
form = res_dict.get('form') 
assert "https://httpbin.org/post" == res_dict.get('url')
assert form and "临渊" == form.get('name') and '123456' == form.get('password')

再次运行,结果和上次一致。没有报错即为assert断言通过,断言失败时会报AssertionError。

发送JSON格式数据

JSON格式是一种通用的数据格式,在Python中JSON实际为“符合JSON语法格式的字符串”,本质是str类型。JSON格式和Python的字典一一对应,略有不同,如JSON中的true/false/null对应字典中的True/False/None。我们同样可以使用字典来构造JSON请求的数据,然后传递够请求方法的json参数即可,示例如下。

import requests
url = 'https://httpbin.org/post'
json_data = {
   
   'name': '临渊', 'age': 18, 'on_site': True, 'favorite': None}
res = requests.post(url, json=json_data)
print(res.text)

URL参数和FORM变动格式中的数字实际都是转为字符串格式去发送的,而JSON中可以区分数字格式和字符串格式。如{"age": 18}{"age":"18"}有可能是不一样的。响应文本打印结果如下。

{
   
   
  "args": {
   
   }, 
  "data": "{\"name\": \"\\u4e34\\u6e0a\", \"age\": 18, \"on_site\": true, \"favorite\": null}", 
  "files": {
   
   }, 
  "form": {
   
   }, 
  "headers": {
   
   
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "70", 
    "Content-Type": "application/json", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.18.4"
  }, 
  "json": {
   
   
    "age": 18, 
    "favorite": null, 
    "name": "\u4e34\u6e0a", 
    "on_site": true
  }, 
  "origin": "111.194.126.253, 111.194.126.253", 
  "url": "https://httpbin.org/post"
}

发送时,请求头中会自动添加"Content-Type": "application/json"
细心的同学会发现,FORM表单格式发送的数据会出现在响应的form字段中,JSON格式的却出现在data字段中。这是因为JSON和XML等格式一样属于Raw(原始格式),即原样发送。但是在实际发送时仍要确保请求数据都转为ASCII(美国标准码)来传输。因此中文参数“临渊”在传输是会按utf-8编码转换为“\u4e34\u6e0a”。由于JSON格式中只能使用双引号,响应中data参数是一个JSON格式的字符串,需要使用转义字符“\”。

发送XML格式的数据

上例提到XML和JSON都属于Raw格式的数据,XML和JSON在Python中实际都是不同格式的文本字符串。我们将字符串传递给请求方法的data参数即可原样发送,即data参数有以下3重作用:

  1. data = {} 或 [(,), (,)]:接受一个字典或嵌套列表格式的数据,会按表单Url编码格式
  2. data = ‘’:接受一个字符串或bytes二进制字符串,会原样发送(需要手动添加请求头,如果存在中文需要手动编码)
  3. data = open(’…’, ‘rb’):接受一个文件对象,按binary格式流式上传。

发送XML格式的数据只要将XML格式的多行字符串传递给请求方法的data参数即可,示例如下。

import requests
url = 'https://httpbin.org/post'
xml_data = '''
<xml>
    <name>临渊</name>
    <age>12</name>
</xml>
'''
headers = {'Content-Type': 'application/xml'}

res = requests.post(url, data=xml_data.encode('utf-8'), headers=headers)
print(res.text)

由于xml_data数据中存在非ASCII码,需要将数据按utf-8格式编码为bytes二进制字符串发送。由于使用Raw格式发送数据时不会自动添加请求头,因此一般要手动在请求头中添加内容类型声明,并将构造的字典类型的请求头变量,传递给请求方法的关键字参数headers。响应结果如下。

{
   
   
  "args": {
   
   }, 
  "data": "\n<xml>\n    <name>\u4e34\u6e0a</name>\n    <age>12</name>\n</xml>\n", 
  "files": {
   
   }, 
  "form": {
   
   }, 
  "headers": {
   
   
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "57", 
    "Content-Type": "application/xml", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.18.4"
  }, 
  "json": null, 
  "origin": "111.194.126.253, 111.194.126.253", 
  "url": "https://httpbin.org/post"
}

Raw格式的数据都会记录在该接口响应数据的data字段中。

Raw格式的请求(Text、JavaScript、JSON、XML、HTML等)都可以按这种方式发送。JSON请求自然也可以按原始方式发送,示例如下。

import requests
url = 'https://httpbin.org/post'
json_data_str = '''
{
    "name": "临渊", 
    "age": 18, 
    "on_site": true, 
    "favorite": null
}
'''
headers = {'Content-Type': 'application/json'}
res = requests.post(url, data=json_data_str.encode('utf-8'), headers=headers)
print(res.text)

注意以上的json_data_str须是符合JSON格式的字符串,包括必须使用双引号,应该使用小写的true,无值应该是null,由于字符串中存在中文,同样要手动进行encode编码,同时要手动添加请求头指定内容类型。

为方便构造请求数据,也可以先构造一个字典格式的请求数据,再使用json.dumps(),将字典格式的数据转为JSON字符串发送,示例如下。

import requests
import json

url = 'https://httpbin.org/post'
json_data = {
   
   
    'name': '临渊', 
    'age': 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值