Contents

Python-第三方库-Requests

PYPI官网

github官网

官网文档

Requests允许您非常轻松地发送 HTTP/1.1 请求。无需手动将查询字符串添加到您的 URL,或对您的 POST 数据进行表单编码。Keep-alive 和 HTTP 连接池是 100% 自动的,基于urllib3。

安装

1
pip install requests

快速入门

发出请求

1
2
3
4
5
6
r = requests.get('https://api.github.com/events')
r = requests.post('https://httpbin.org/post', data={'key': 'value'})
r = requests.put('https://httpbin.org/put', data={'key': 'value'})
r = requests.delete('https://httpbin.org/delete')
r = requests.head('https://httpbin.org/get')
r = requests.options('https://httpbin.org/get')

在 URL 中传递参数

针对httpbin.org/get?key=val类型参数

1
2
payload = {'key1': 'value1', 'key2': 'value2'}
r = requests.get('https://httpbin.org/get', params=payload)

打印url

1
print(r.url)

任何值为None的字典键都不会添加到 URL 的查询字符串中

响应内容

读取服务器响应的内容

1
r.text#'[{"repository":{"open_issues":0,"url":"https://github.com/...

默认提交给服务器的编码类型是unicode,可以修改服务器编码类型

1
2
r.encoding#'utf-8'
r.encoding = 'ISO-8859-1'

二进制响应内容

非文本请求,以字节形式访问响应主体

1
r.content#b'[{"repository":{"open_issues":0,"url":"https://github.com/...

默认解码格式是gzip

如果安装了像brotli或brotlicffi这样的 Brotli 库,br 传输编码会自动为您解码

例如,要从请求返回的二进制数据创建图像

1
2
3
4
from PIL import Image
from io import BytesIO

i = Image.open(BytesIO(r.content))

JSON 响应内容

requests 内置了 JSON 解码器

1
r.json()#[{'repository': {'open_issues': 0, 'url': 'https://github.com/...

如果解码失败,r.json()引发异常,抛出requests.exceptions.JSONDecodeError。例如,如果响应获得 204(无内容),或者如果响应包含无效的 JSON。

原始响应内容

从服务器获取原始套接字响应,进行流式传输,设置stream=True

1
2
3
r = requests.get('https://api.github.com/events', stream=True)
r.raw#<urllib3.response.HTTPResponse object at 0x101194810>
r.raw.read(10)

保存流式传输到文件

1
2
3
with open(filename, 'wb') as fd:
    for chunk in r.iter_content(chunk_size=128):
        fd.write(chunk)

自定义标题

1
2
3
4
url = 'https://api.github.com/some/endpoint'
headers = {'user-agent': 'my-app/0.0.1'}

r = requests.get(url, headers=headers)

更复杂的 POST 请求

1
2
3
payload_dict = {'key1': ['value1', 'value2']}
r2 = requests.post('https://httpbin.org/post', data=payload_dict)
print(r1.text)

data 可以传入 字符串 和 字典。字典将自动编码为json字符串

添加文件

1
2
3
4
url = 'https://httpbin.org/post'
files = {'file': open('report.xls', 'rb')}

r = requests.post(url, files=files)

显式设置文件名、内容类型和标题

1
2
3
4
url = 'https://httpbin.org/post'
files = {'file': ('report.xls', open('report.xls', 'rb'), 'application/vnd.ms-excel', {'Expires': '0'})}

r = requests.post(url, files=files)

发送要作为文件接收的字符串

1
2
3
4
url = 'https://httpbin.org/post'
files = {'file': ('report.csv', 'some,data,to,send\nanother,row,to,send\n')}

r = requests.post(url, files=files)

非常大的文件作为multipart/form-data 请求发送需要流式传输该请求。默认情况下,requests不支持这个,但是有一个单独的包 requests-toolbelt支持。

响应状态代码

1
2
r = requests.get('https://httpbin.org/get')
r.status_code#200

内置的状态码查找对象

1
r.status_code == requests.codes.ok#True

判断状态码是否正常的工具函数,如果错误将抛出requests.exceptions.HTTPError异常,否则返回None

1
r.raise_for_status()

响应头

返回一个字典

1
r.headers

响应的cookie

1
r.cookies

将自己的 cookie 发送到服务器

1
2
3
4
url = 'https://httpbin.org/cookies'
cookies = dict(cookies_are='working')

r = requests.get(url, cookies=cookies)

使用cookieJar管理cookie

1
2
3
4
5
jar = requests.cookies.RequestsCookieJar()
jar.set('tasty_cookie', 'yum', domain='httpbin.org', path='/cookies')
jar.set('gross_cookie', 'blech', domain='httpbin.org', path='/elsewhere')
url = 'https://httpbin.org/cookies'
r = requests.get(url, cookies=jar)

重定向和历史

可以通过历史重定向的请求

1
2
3
4
r = requests.get('http://github.com/')
r.url#'https://github.com/
r.status_code#200
r.history#[<Response [301]>]

GET、OPTIONS、POST、PUT、PATCH 或 DELETE默认启用重定向,使用allow_redirects参数禁用重定向处理

1
2
3
r = requests.get('http://github.com/', allow_redirects=False)
r.status_code#301
r.history#[]

HEAD默认禁用重定向,使用allow_redirects参数启用重定向处理

1
2
3
4
r = requests.head('http://github.com/', allow_redirects=True)
r.url#'https://github.com/
r.status_code#200
r.history#[<Response [301]>]

超时

几乎所有生产代码都应在几乎所有请求中使用此参数。不这样做可能会导致您的程序无限期挂起。这个超时不是整个交互过程超时,而是接收第一个字节超时

1
requests.get('https://github.com/', timeout=0.001)

错误和异常

Requests 显式引发的所有异常都继承自 requests.exceptions.RequestException.

  • 出现网络问题(例如 DNS 故障、拒绝连接等),引发ConnectionError异常
  • HTTP 请求返回不成功的状态代码,引发HTTPError异常
  • 请求超时,引发Timeout异常
  • 请求超过配置的最大重定向数, 引发TooManyRedirects异常

高级用法

会话对象

Session 对象允许您跨请求保留某些参数。它还会在 Session 实例发出的所有请求中保留 cookie,并将使用urllib3的连接池。底层的 TCP 连接将被重用,这可能会显着提高性能。

1
2
3
4
s = requests.Session()

s.get('https://httpbin.org/cookies/set/sessioncookie/123456789')
r = s.get('https://httpbin.org/cookies')

会话也可用于向请求方法提供默认数据:

1
2
s.auth = ('user', 'pass')
s.headers.update({'x-test': 'true'})

传递给请求方法级字典都将与设置的会话级值合并,方法级参数覆盖会话参数

会话也可以用作上下文管理器:

1
2
with requests.Session() as s:
    s.get('https://httpbin.org/cookies/set/sessioncookie/123456789')

从字典参数中删除一个值:在单次请求中会话级字典的键,只需在方法级参数中将该键的值设置为None

请求和响应对象

1
2
r.headers#响应头
r.request.headers#请求头

准备好的请求

每次API 调用或会话调用发送的请求其实就是一个PreparedRequest对象,r.request也是这个对象。所以通过在发送前修改这个对象能够实现对正文或标头(或其他任何内容)做一些额外的工作

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
req = Request('POST', url, data=data, headers=headers)
prepped = req.prepare()

# do something with prepped.body
prepped.body = 'No, I want exactly this as the body.'

# do something with prepped.headers
del prepped.headers['Content-Type']

resp = s.send(prepped,
    stream=stream,
    verify=verify,
    proxies=proxies,
    cert=cert,
    timeout=timeout
)

上面的代码无法使用session对象配置的会话级属性,使用Session.prepare_request()替换Request.prepare()

1
prepped = s.prepare_request(req)

SSL 证书验证

默认情况下,启用 SSL 验证,如果无法验证证书,Requests 将抛出 SSLError:

1
requests.get('https://requestb.in')#requests.exceptions.SSLError: hostname 'requestb.in' doesn't match either of '*.herokuapp.com', 'herokuapp.com'

可以将verify路径传递给带有受信任 CA 证书的 CA_BUNDLE 文件或目录

1
2
3
4
requests.get('https://github.com', verify='/path/to/certfile')

s = requests.Session()
s.verify = '/path/to/certfile'

如果verify设置为目录路径,则必须使用 OpenSSL 提供的实用程序c_rehash处理该目录。

当verify设置为时False,请求将接受服务器提供的任何 TLS 证书,并将忽略主机名不匹配和/或过期的证书

客户端证书

指定本地证书用作客户端证书,使用单个文件(包含私钥和证书)或两个文件路径的元组

1
2
3
4
5
requests.get('https://kennethreitz.org', cert=('/path/client.cert', '/path/client.key'))
<Response [200]>

s = requests.Session()
s.cert = '/path/client.cert'

指定错误的路径或无效的证书,抛出 SSLError

本地证书的私钥必须未加密。目前,Requests 不支持使用加密密钥

CA 证书

Requests 使用包certifi中的证书。当certifi未安装时,这会导致在使用明显较旧版本的 Requests 时出现极其过时的证书包。为了安全起见,我们建议经常升级 certifi!

正文内容工作流程

默认情况下,当您发出请求时,会立即下载响应正文。

覆盖此行为并推迟下载响应主体,直到您使用stream参数访问Response.content 属性:

1
2
tarball_url = 'https://github.com/psf/requests/tarball/main'
r = requests.get(tarball_url, stream=True)

此时只有响应标头已被下载并且连接保持打开状态,可以根据响应头信息决定下载正文:

1
2
3
if int(r.headers['content-length']) < TOO_LONG:
  content = r.content
  ...

可以使用Response.iter_content() 和Response.iter_lines()方法进一步控制工作流

如果您在发出请求时设置stream为True,除非您消耗所有数据或调用Response.close,否则 Requests 无法将连接释放回池中 。这可能导致连接效率低下。在使用stream=True时部分阅读了请求正文(或根本没有阅读),您应该在with声明中发出请求以确保它始终关闭:

1
2
with requests.get('https://httpbin.org/get', stream=True) as r:
    # Do things with the response here.

保持活动

keep-alive 在一个会话中是 100% 自动的

流媒体上传

Requests 支持流式上传,这允许您发送大型流或文件而无需将它们读入内存。要流式传输和上传,只需为您的身体提供一个类似文件的对象:

1
2
with open('massive-body', 'rb') as f:
    requests.post('http://some.url/streamed', data=f)

块编码请求

Requests 还支持传出和传入请求的分块传输编码,要发送块编码的请求,只需为您的正文提供一个生成器(或任何没有长度的迭代器)

1
2
3
4
5
def gen():
    yield 'hi'
    yield 'there'

requests.post('http://some.url/chunked', data=gen())

对于分块编码的响应,最好使用 Response.iter_content()并设置stream=True

发送多个多部分编码文件

将文件设置为(form_field_name, file_info)元组列表:

1
2
3
4
5
url = 'https://httpbin.org/post'
multiple_files = [
    ('images', ('foo.png', open('foo.png', 'rb'), 'image/png')),
    ('images', ('bar.png', open('bar.png', 'rb'), 'image/png'))]
r = requests.post(url, files=multiple_files)

事件挂钩Hook

Requests 有一个挂钩系统,您可以使用它来操作部分请求过程或信号事件处理

您可以通过将字典传递给请求参数来为每个请求分配一个挂钩函数。回调函数必须处理自己的异常。如果回调函数返回一个值,将替换传入的数据。如果函数不返回任何内容,则没有影响。可以向单个请求添加多个挂钩。

1
2
3
4
5
6
7
8
def print_url(r, *args, **kwargs):
    print(r.url)

def record_hook(r, *args, **kwargs):
    r.hook_called = True
    return r

r = requests.get('https://httpbin.org/', hooks={'response': [print_url, record_hook]})

可以向Session实例添加挂钩

1
2
3
s = requests.Session()
s.hooks['response'].append(print_url)
s.get('https://httpbin.org/')

自定义认证

Requests 允许指定自己的身份验证机制

身份验证实现是AuthBase的子类。Requests 在requests.auth中提供了两种常见的身份验证方案实现:HTTPBasicAuth和HTTPDigestAuth

  • class requests.auth.HTTPBasicAuth(username, password):Attaches HTTP Basic Authentication to the given Request object.
  • lass requests.auth.HTTPDigestAuth(username, password):Attaches HTTP Digest Authentication to the given Request object.

自定义身份认证:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from requests.auth import AuthBase

class PizzaAuth(AuthBase):
    """Attaches HTTP Pizza Authentication to the given Request object."""
    def __init__(self, username):
        # setup any auth-related data here
        self.username = username

    def __call__(self, r):
        # modify and return the request
        r.headers['X-Pizza'] = self.username
        return r

requests.get('http://pizzabin.org/admin', auth=PizzaAuth('kenneth'))

其实就是在__call__方法里面修改请求头

流媒体请求

Response.iter_lines()您可以轻松地迭代流式 API。只需设置stream True并迭代响应 iter_lines:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import json
import requests

r = requests.get('https://httpbin.org/stream/20', stream=True)

for line in r.iter_lines():

    # filter out keep-alive new lines
    if line:
        decoded_line = line.decode('utf-8')
        print(json.loads(decoded_line))

将decode_unicode=True与 Response.iter_lines()或Response.iter_content() 一起使用时,如果服务器不提供后备编码,需要指定编码:

1
2
3
4
5
6
7
8
r = requests.get('https://httpbin.org/stream/20', stream=True)

if r.encoding is None:
    r.encoding = 'utf-8'

for line in r.iter_lines(decode_unicode=True):
    if line:
        print(json.loads(line))

代理

使用proxies参数配置单独的请求或Session:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
proxies = {
  'http': 'http://10.10.1.10:3128',
  'https': 'http://10.10.1.10:1080',
}

requests.get('http://example.org', proxies=proxies)

session = requests.Session()
session.proxies.update(proxies)
session.get('http://example.org')

session.proxies的行为可能与预期不同。提供的值将被环境代理(由urllib.request.getproxies返回的那些)覆盖。为确保在存在环境代理的情况下使用代理,请明确指定proxies所有单独请求的参数。

代理有HTTP Basic Auth时,需要使用账号密码配置proxy为如下形式:

1
2
# http://user:password@host/
proxies = {'http': 'http://user:pass@10.10.1.10:3128/'}

要为特定方案和主机提供代理,请使用 scheme://hostname形式作为键。这将匹配对给定方案和确切主机名的任何请求。

1
proxies = {'http://10.20.1.128': 'http://10.10.1.10:5323'}

使用代理进行 https 连接通常需要您的本地计算机信任代理的根证书,请求信任的证书列表可以通过以下方式找到

1
print(DEFAULT_CA_BUNDLE_PATH)

将REQUESTS_CA_BUNDLE (或CURL_CA_BUNDLE)环境变量设置为另一个文件路径来覆盖此默认证书包

1
export REQUESTS_CA_BUNDLE="/usr/local/myproxy_info/cacert.pem"

SOCKS

Requests 还支持使用 SOCKS 协议的代理

需要安装第三方库

1
python -m pip install requests[socks]

安装这些依赖项后,使用 SOCKS 代理就像使用 HTTP 代理一样简单:

1
2
3
4
proxies = {
    'http': 'socks5://user:pass@host:port',
    'https': 'socks5://user:pass@host:port'
}

阻塞还是非阻塞

Requests 不提供任何类型的非阻塞 IO

超时

默认只配置connect超时,但是也可以通过第二个参数配置read超时

1
r = requests.get('https://github.com', timeout=(3.05, 27))
 |