本文链接:

Python3 学习笔记(目录)

1. HTTPX

httpx 模块是 Python 3 的全功能 HTTP 客户端,它提供了 同步 和 asyncio 异步 API,并支持 HTTP/1.1 和 HTTP/2

httpx 是一个高性能的异步 HTTP 客户端,它建立在 requests 完善的可用性之上,支持 连接和连接池的保持、Cookie 持久性会话、自动内容解码、Basic/Digest 身份认证、HTTP(S) 代理、分段文件上传、分块请求 等一系列功能。httpx 的 API 布局大部分遵循了 requests,但比后者更强大,是用于 Python 的下一代 HTTP 客户端。

相关链接:

HTTP 请求测试用地址: http://httpbin.org/https://httpbin.org/

HTTPX 需要 Python 3.6+

安装 httpx 模块:

$ pip3 install httpx

一些可选安装:

# 包含 HTTP/2 的支持
$ pip3 install httpx[http2]

# brotli 解码器支持
$ pip3 install httpx[brotli]

# 命令行使用 httpx
$ pip3 install httpx[cli]

# SOCKS 代理支持
$ pip3 install httpx[socks]

2. HTTPX 的请求方法

HTTP 请求方法:

httpx.get()                 # GET 请求
httpx.post()                # POST 请求
httpx.put()                 # PUT 请求
httpx.patch()               # PATCH 请求
httpx.head()                # HEAD 请求
httpx.options()             # OPTIONS 请求
httpx.delete()              # DELETE 请求

以上各请求最终都发到 httpx.request() 方法(参数列表也与之相似),并返回 httpx.Response 对象。

httpx.request(method, url, …) 方法的参数说明:

# HTTP 请求方法
httpx.request(
    method: str,
    url: URLTypes,
    *,
    params: QueryParamTypes = None,
    content: RequestContent = None,
    data: RequestData = None,
    files: RequestFiles = None,
    json: typing.Any = None,
    headers: HeaderTypes = None,
    cookies: CookieTypes = None,
    auth: AuthTypes = None,
    proxies: ProxiesTypes = None,
    timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
    allow_redirects: bool = True,
    verify: VerifyTypes = True,
    cert: CertTypes = None,
    trust_env: bool = True,
) -> httpx.Response

# 参数说明:
#       除了 method 和 url 参数, 其他都是可选参数。
#
# method:
#       请求方法: "GET", "POST", "PUT", "PATCH", "HEAD", "OPTIONS", or "DELETE"
#
# url:
#       请求链接, 可以是 str 类型。
#
# params:
#       URL查询参数, 拼接到 URL Query String 中。
#
#       类型: 
#           str:         "name1=value1&name2=value2"
#           dict:        {name1: value1, name2: value2, ...}
#           list[tuple]: [(name1, value1), (name2, value2), ...]
#
#       拼接后结果如: http://httpbin.org/get?name1=value1&name2=value2
#
# content:
#       要包含在请求 Body 中的二进制内容, 类型为 bytes 或 byte iterator。
#       byte iterator 可以是以 二进制打开的文件 IO 对象, 可用此参数直接上传文件。
#
# data:
#       要包含在请求 Body 中的 form 表单数据, dict 类型。
#       例如: data={name1: value1, name2: value2}
#       将按 form 表单格式拼接后发送到 请求 Body 中, 如: "name1=value1&name2=value2"
#       并自动添加请求头: "Content-Type": "application/x-www-form-urlencoded"
#
# files:
#       要包含在请求 Body 中 form 表单文件字段, dict 类型。
#       可结合 data 参数同时上传 form 表单的 普通字段 和 文件字段。
#
#       使用此参数, 自动添加请求头:
#           "Content-Type": "multipart/form-data; boundary=xxx"
#
#       参数格式:
#           {"field_name": file-like-objects}
#           {"field_name": ("filename", file-like-objects)}
#           {"field_name": ("filename", file-like-objects, "content_type")}
#
#       字段说明:
#           field_name: form 表单中文件字段的字段名
#           file-like-objects: 已打开的二进制文件 IO 流对象(上次完毕后需手动关闭流)
#           filename: 显式地设置 此文件字段的 文件名(默认使用本地文件名)
#           content_type: 显式地设置 此文件字段的 文件类型(默认根据文件名后缀设置)
#
#           其中 file-like-objects 可以用 str/bytes 来代替文件流的内容,
#           可以同时上传多个文件。
#
#       参数传值示例:
#           {"f1": open("aa.jpg", "rb")} 提交后表单字段值为: name=f1, filename=aa.jpg
#           {"f1": open("aa.jpg", "rb"), "f2": open("bb.png", "rb")} 同时上传多个文件
#           {"f1": ("bb.jpg", open("aa.jpg", "rb"))} 提交后表单字段值为: name=f1, filename=bb.jpg
#
# json:
#       要包含在请求 Body 中的 JSON 可序列化对象(如 dict 类型),
#       序列为为 JSON 字符串后发送到请求 Body 中。
#
#       使用此参数, 自动添加请求头:
#           "Content-Type": "application/json"
#
#       参数示例:
#           json={"name": "tom", "age": 30}
#
# headers:
#       要包含在请求头中的 HTTP 请求头, 会覆盖现有的请求头, 一般为 dict 类型。
#
#       参数示例:
#           headers={"Header1": "Value1", "Header2": "Value2"}
#
# cookies:
#       要包含在请求头中的 Cookies, dict 或 httpx.Cookies 或 http.cookiejar.CookieJar 类型,
#       如果传递的是 CookieJar 对象, 则从中提取匹配此 URL 请求的 Cookie 添加到请求头中。
#
#       参数示例:
#           cookies={"name1": "value1", "name2": "value2"}
#
# auth:
#       发送请求时使用的身份认证。
#       HTTP Auth 认证类型: Basic、Digest、Custom 等
#
#       参数示例:
#           Basic Auth:  auth=httpx.BasicAuth("user", "pass")
#                        或 
#                        auth=("user", "pass")
#           Digest Auth: auth=httpx.DigestAuth("user", "pass")
#
#       使用示例:
#           resp = httpx.get("https://httpbin.org/basic-auth/user/pass", auth=("user", "pass"))
#
# proxies:
#       使用代理请求, str 或 dict 类型。
#       
#       参数示例:
#           # 所有请求发送到 http://localhost:8080 代理
#           proxies="http://localhost:8080"
#
#           # 需要认证的代理连接格式
#           proxies="http://user:passwd@localhost:8080"
#
#           # proxy_key 为 URL 前缀, 匹配此前缀的 URL 发送到 proxy_url 代理。
#           # proxy_key 值可以为 "http://"、"http://httpbin.org" 等。
#           # proxy_key 只包括 protocol://hostname[:port], 不包括 path,
#           # proxy_key 只匹配到 protocol://hostname[:port], 不匹配 path.
#           proxies={"proxy_key": "proxy_url"}
#
#           proxies={
#               "http://": "http://localhost:8080",  # http://* 格式的请求发送到指定代理
#               "https://": "http://localhost:8080"  # https://* 格式的请求发送到指定代理
#           }
#
# timeout:
#       超时时间, float 或 httpx.Timeout 类型。
#       用 float 类型则表示所有操作的超时时间均为此时间, 默认均为 5.0 秒。
#       None 表示不检查超时。
#
#       可以创建 httpx.Timeout 对象更细致的控制超时时间:
#           Timeout(
#               timeout=None,       # 其他为 None 的字段的默认超时时间
*               *,
#               connect=None,       # 连接超时时间
#               read=None,          # 读取超时时间
#               write=None,         # 写入超时时间
#               pool=None           # 从连接池中获取连接的超时时间
#           )
#
#           Timeout(None)               # 所有操作不超时
#           Timeout(5.0)                # 所有操作 5 秒超时
#           Timeout(None, connect=5.0)  # 连接 5 秒超时, 其他操作不超时
#           Timeout(5.0, read=10.0)     # 读取 10 秒超时, 其他操作 5 秒超时
#
#       超时将抛出对应的异常:
#           httpx.ConnectTimeout
#           httpx.ReadTimeout
#           httpx.WriteTimeout
#           httpx.PoolTimeout
#
# allow_redirects:
#       启用 或 禁用 HTTP 重定向, bool 类型, 默认为 True 启用。
#
# verify:
#       是否校验 SSL 证书, 用于 HTTPS 请求时验证所请求主机的身份, 有内置信任的根证书。
#       类型 为 str, bool 或 ssl.SSLContext。
#       默认为 True(校验), False 表示不校验(不安全)。
#       可以传递 str 类型指定 SSL 证书文件的路径,
#       或传递 ssl.SSLContext 的实例。
#
#       没有被 httpx 信任的 SSL 证书, 可以手动指定信任, 示例:
#           verify=/my-ca-cert.pem 
#           或
#           verify=/my-ca-cert.cer
#
#       一个 pem/cer 证书文件中可以有多个证书。
#
# cert:
#       被请求的主机用来验证客户端的 SSL 证书。
#       类型为:
#           str: SSL certificate file path
#           两元组: (certificate file, key file)
#           三元组: (certificate file, key file, password)
#
# trust_env:
#       启用或禁用环境变量用于配置

httpx.request() 会把响应正文一次性读取到内存,如果下载大文件,需要使用 httpx.stream() 流式响应 请求:

# 流式响应请求, 返回一个响应迭代对象
httpx.stream(
    method: str,
    url: URLTypes,
    *,
    params: QueryParamTypes = None,
    content: RequestContent = None,
    data: RequestData = None,
    files: RequestFiles = None,
    json: typing.Any = None,
    headers: HeaderTypes = None,
    cookies: CookieTypes = None,
    auth: AuthTypes = None,
    proxies: ProxiesTypes = None,
    timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
    allow_redirects: bool = True,
    verify: VerifyTypes = True,
    cert: CertTypes = None,
    trust_env: bool = True,
) -> typing.Iterator[Response]

2.1 GET 请求示例

import httpx

resp = httpx.get("https://httpbin.org/get")

print(resp.status_code)                     # HTTP 状态码
print(resp.headers)                         # 响应头
print(resp.cookies)                         # 响应的 cookies
print(resp.text)                            # str 类型的响应内容
print(resp.content.decode("utf-8"))         # bytes 类型的响应内容

2.2 POST 请求示例

import httpx

resp = httpx.post("https://httpbin.org/post", content=b"Hello World")

print(resp.status_code)
print(resp.text)

2.3 提交 form 表单示例

import httpx

file1 = open("aa.txt", "rb")

resp = httpx.post("https://httpbin.org/post",
                  data={"key1": "value1"},
                  files={"file_key1": file1}
                  )

print(resp.content.decode("utf-8"))

file1.close()

3. 响应对象: httpx.Response

httpx.request() 发出请求后,返回一个响应对象 httpx.Response,包含了 响应状态、响应头、响应 Body 等各类信息。

httpx.Response 类的属性和方法:

# 构造方法
def __init__(...)


# HTTP 状态码, 如: 200, 302, 404
status_code: int


# 原因短语, 如: OK, FOUND, NOT FOUND
reason_phrase: str


# HTTP 版本, 如: "HTTP/2"、"HTTP/1.1"
http_version: str


# 请求的 URL
url: httpx.URL


# 响应头
# 获取响应头的值: headers.get(key), headers[key]
# 遍历所有响应头: headers.keys(), headers.items()
headers: httpx.Headers


# 响应内容, bytes 类型。
# 如果响应还没有 read(), 则引发 ResponseNotRead。
# 请求封装为 Request 对象由 Client 发送请求时, 有个 stream 参数。
# 如果 stream=False (默认) 则响应成功后自动 read() 响应内容。
# 如果 stream=True, 则不会主动 read() 响应内容。
# httpx.get(), httpx.post() 等直接请求的方法默认 stream=False。
# 下载大文件时需要使用 Client(stream=True) 或 httpx.stream() 发送请求, 然后通过迭代响应逐块读取。
content: bytes


# 把 content 响应内容按 encoding 解码为文本
text: str


# 响应的内容编码
encoding: str


# 是否为重定向
is_redirect: bool


# 响应的 Cookie
# 获取 Cookie: cookies.get(key), cookies[key]
# 遍历 Cookie: cookies.keys(), cookies.items()
cookies: httpx.Cookies


# 该响应的请求对象, 所有请求会先封装成 Request 对象再由 Client 发送请求
request: httpx.Request


# 下一个请求。
# 如果 allow_redirects=False (不自动处理重定向) 且响应为重定向, 则 next_request 为重定向的 Request。
# 如果 allow_redirects=True (默认), 即自动处理重定向请求, 则该值为 None。
next_request: httpx.Request


# 响应的历史, 当请求被自动重定向时记录之前的响应。
# 例如当前 Response (200) 是重定向后的响应, 
# 则 history 为自动触发当前响应的前面的 Response (30X)
history: list[httpx.Response]


# 返回完成整个请求/响应周期所需的时间。
# 从发送请求到接收到响应并读取完所有响应内容调用 close() 关闭响应释放连接后所需的时间。
# elapsed.total_seconds() 可以获取准确的所需时间秒数。
elapsed: datetime.timedelta


# 根据 状态码(4XX/5XX) 引发 HTTPStatusError, 
# 如果没有错误不做任何操作。
# HTTP 状态码: https://httpstatuses.com/
def raise_for_status() -> None


# 将 content 解析为 JSON (dict) 返回,
# 响应内容必须为 JSON 格式, 否则引发 JSONDecodeError
def json() -> dict


# 读取响应内容 (一次性读取全部), 如果是下载大文件需要通过迭代逐块读取。
def read() -> bytes


# 迭代读取响应内容 (未经 gzip/deflate/brotli 解压的原始内容, 不推荐使用)。
# chunk_size 为每次迭代返回的最大块大小 (下同), 默认为 None 表示一次迭代完所有。
def iter_raw(chunk_size: int = None) -> bytes iterator


# 迭代读取响应内容 (经过 gzip/deflate/brotli 解压后的内容, 推荐使用), 
# 内部实际是通过 iter_raw() 迭代后再解压。
def iter_bytes(chunk_size: int = None) -> bytes iterator


# 以文本方式迭代读取响应内容 (使用 encoding 解码), 
# 内部实际是通过 iter_bytes() 迭代后再解码。
def iter_text(chunk_size: int = None) -> text iterator


# 以文本方式逐行迭代读取响应内容,
# 内部实际是通过 iter_text() 迭代后再解析为行。
def iter_lines() -> text iterator


# 关闭响应 并 释放连接。
# 不需要主动调用, 当响应内容被读取完毕后会自动调用。
def close() -> None


# 异步版本的 read()
async def aread() -> bytes


# 异步版本的 iter_raw()
async def aiter_raw(chunk_size: int = None) -> bytes iterator


# 异步版本的 iter_bytes()
async def aiter_bytes(chunk_size: int = None) -> bytes iterator


# 异步版本的 iter_text()
async def aiter_text(chunk_size: int = None) -> text iterator


# 异步版本的 iter_lines() 
async def aiter_lines() -> text iterator


# 异步版本的 close()
# 不需要主动调用, 当响应内容被读取完毕后会自动调用。
async def aclose() -> None

3.1 GET 请求示例:

import httpx

resp = httpx.get("https://httpbin.org/cookies/set/hello/world", allow_redirects=False)

print(resp.status_code)                     # 302
print(resp.reason_phrase)                   # "FOUND"
print(resp.http_version)                    # "HTTP/1.1"

for header in resp.headers.items():         # 迭代响应头
    print(f"{header[0]}: {header[1]}")

for cookie in resp.cookies.items():         # 迭代 Cookie
    print(f"{cookie[0]} = {cookie[1]}")     # "hello = world"

print(resp.read().decode())                 # 输出响应内容

3.2 下载大文件: 流式响应

对于大型下载,你可能希望使用不会一次将整个响应正文加载到内存中的 流式响应

下载文件示例:

import httpx

client = httpx.Client()

req = httpx.Request("GET", "https://example/aa.zip")

resp = client.send(req, stream=True)

if resp.status_code == 200:
    with open("aa.zip", "wb") as f:
        for buf in resp.iter_bytes(1024):
            f.write(buf)

或使用直接使用 httpx.stream() 流式响应下载:

import httpx

with httpx.stream("GET", "https://example/aa.zip") as resp:
    with open("aa.zip", "wb") as f:
        for buf in resp.iter_bytes(1024):
            f.write(buf)

httpx.stream() 内部也是通过创建 httpx.Client() 然后 client.send(req, stream=True) 发出请求。

4. 请求对象: httpx.Request

httpx 发出的所有请求最终都是先封装为 httpx.Request 再由 Client 发送请求。

httpx.Request 类的属性和方法:

# 构造方法, 参数含义与 httpx.request() 方法中的参数含义相同。
# 其中 stream 为发送到请求 Body 中的字节流, 
# 如果传递了 stream 参数, 则会忽略 content, data, files, json 这几个参数, 并且需要自行构建相应的请求头。
class httpx.Request(
        method: typing.Union[str, bytes],
        url: typing.Union["URL", str, RawURL],
        *,
        params: QueryParamTypes = None,
        headers: HeaderTypes = None,
        cookies: CookieTypes = None,
        content: RequestContent = None,
        data: RequestData = None,
        files: RequestFiles = None,
        json: typing.Any = None,
        stream: typing.Union[SyncByteStream, AsyncByteStream] = None,
    )

代码示例:

import httpx

client = httpx.Client()

req = httpx.Request("GET", "https://httpbin.org/get")

resp = client.send(req)

print(resp.text)

5. 保存/加载本地 Cookie

响应的 Cookie 可以保存到本地文件,下次再从本地文件加载使用:

import httpx
import http.cookiejar

# 创建一个 CookieJar 对象, 关联本地文件
cookiejar = http.cookiejar.MozillaCookieJar()
# 加载本地文件中的 Cookie(文件必须存在)
# cookiejar.load("cookies.txt")
# 当前当做是新的会话, 可以清除掉旧的会话 Cookie
# cookiejar.clear_session_cookies()

# 创建 httpx 的 Cookies 对象(Cookie 将最终保存到 cookiejar 中)
cookies = httpx.Cookies(cookiejar)

# GET 请求, 自动携带上匹配 URL 的 Cookie
resp = httpx.get("https://www.baidu.com/", cookies=cookies)
print(resp.text)

# 更新响应中的新 Cookie 到 cookies(最终被更新到 cookiejar)
for his_resp in resp.history:
    cookies.update(his_resp.cookies)    # 防止是重定向后的响应, 前面如果有响应也要添加/更新对应的 Cookie
cookies.update(resp.cookies)            # 添加/更新本次响应的 Cookie

# 保存 cookiejar 中的 Cookies 到本地文件
cookiejar.save("cookies.txt")

6. 同步 HTTP 客户端: httpx.Client

使用 httpx.get(), httpx.request() 等直接发送 HTTP 请求的方式是先内部封装好请求对象,然后再创建一个 HTTP 客户端对象 (httpx.Client),然后由该客户端发送请求,响应完毕后立即关闭客户端。httpx.request() 每发一个请求都需要频繁地重新建立 TCP 连接、发送 HTTP 请求、接收响应、关闭 TCP 链接,而且还不能自动保持 Cookies,对于相同域名的 URL 请求则会显得非常低效。因此如果需要频繁发送请求,需要先创建一个 HTTP 客户端,然后统一由此客户端发送请求。

httpx.Client 是一个 HTTP 客户端,具有 连接池保持连接HTTP/2Cookie 持久性 等功能。

HTTP/2 最大优势之一是支持相同域名的 URL 请求复用同一个 TCP 连接(前提是使用同一个连接池),不需要每个请求都频繁建立 TCP 连接,效率明显提高。目前大多数主流网站都已支持 HTTP/2。HTTP/2 介绍:https://http2.github.io/

6.1 httpx.Client 的构造方法

httpx.Client 类的构造方法:

class httpx.Client(
        *,
        auth: AuthTypes = None,
        params: QueryParamTypes = None,
        headers: HeaderTypes = None,
        cookies: CookieTypes = None,
        verify: VerifyTypes = True,
        cert: CertTypes = None,
        http2: bool = False,
        proxies: ProxiesTypes = None,
        mounts: typing.Mapping[str, BaseTransport] = None,
        timeout: TimeoutTypes = Timeout(timeout=5.0),
        limits: Limits = Limits(max_connections=100, 
                                max_keepalive_connections=20, 
                                keepalive_expiry=5.0),
        max_redirects: int = 20,
        event_hooks: typing.Mapping[str, typing.List[typing.Callable]] = None,
        base_url: URLTypes = "",
        transport: BaseTransport = None,
        app: typing.Callable = None,
        trust_env: bool = True,
    )

# 参数说明:
#       部分参数的用法与 httpx.request() 方法中的参数用法相同, 不再细说。
#       httpx.Client 中的参数最终会 全局应用(合并追加)到每一个 HTTP 请求,
#       作为每一个 HTTP 请求的默认参数(相同参数会被具体请求时传递的参数覆盖)。
#
# auth: 
#       发送请求时使用的身份验证
#
# params:
#       要包含在请求 URL 中的查询参数, 作为字符串、字典或二元组序列。
#
# headers:
#       发送请求时要包含的 HTTP 请求头字典。
#
# cookies:
#       发送请求时要包含的 Cookie, dict 或 httpx.Cookies 或 http.cookiejar.CookieJar 类型。
#       如果传递的是 dict, 则 Cookie 会添加到每一个 URL 请求。
#       如果传递的是 CookieJar 对象, 则从中提取匹配 URL 请求的 Cookie 添加到请求头中。
#
# verify:
#       是否校验 SSL 证书, 默认校验。
#
# cert:
#       被请求的主机用来验证客户端的 SSL 证书。
#
# http2:
#       是否开启 HTTP/2, 需要开启后才能使用 HTTP/2
#
# proxies:
#       使用代理请求
#
# mounts:
#       mounts/挂载
#
# timeout:
#       超时时间, 默认均为 5.0 秒
#
# limits: Limits = Limits(max_connections=100, 
#       要使用的限制配置, httpx.Limits 类型。                                
#       默认为:
#           Limits(
#               max_connections=100,            # 最大并发连接数
#               max_keepalive_connections=20,   # 连接池最大保持的连接数
#               keepalive_expiry=5.0            # 保持的连接的过期时间, 默认 5 秒
#           )
#
# max_redirects:
#       最大重定向响应数, 默认为 20
#
# event_hooks:
#       事件钩子
#       
# base_url:
#       基础 URL, 发送请求的 URL 如果是相对路径, 则拼接到 base_url 后面形成绝对路径再发请求。
#
#
# transport:
#       用于通过网络发送请求的传输类
#
# app:
#       一个用于发送请求的 WSGI 应用程序,而不是发送实际的网络请求。
#
# trust_env
#       启用或禁用环境变量用于配置

6.2 httpx.Client 的请求方法

httpx.Client 对象的 HTTP 请求方法:

client.get()                    # GET 请求
client.post()                   # POST 请求
client.put()                    # PUT 请求
client.patch()                  # PATCH 请求
client.head()                   # HEAD 请求
client.options()                # OPTIONS 请求
client.delete()                 # DELETE 请求

以上各请求最终都发到 client.request() 方法(参数列表也与之相似),并返回 httpx.Response 对象。

httpx.Client.request() 方法的参数:

# 方法参数的使用与 httpx.request() 相同
httpx.Client.request(
        method: str,
        url: URLTypes,
        *,
        content: RequestContent = None,
        data: RequestData = None,
        files: RequestFiles = None,
        json: typing.Any = None,
        params: QueryParamTypes = None,
        headers: HeaderTypes = None,
        cookies: CookieTypes = None,
        auth: typing.Union[AuthTypes, UnsetType] = UNSET,
        allow_redirects: bool = True,
        timeout: typing.Union[TimeoutTypes, UnsetType] = UNSET,
    ) -> httpx.Response:

httpx.Client 的其他方法:

# 流式响应请求
httpx.Client.stream(
        method: str,
        url: URLTypes,
        *,
        content: RequestContent = None,
        data: RequestData = None,
        files: RequestFiles = None,
        json: typing.Any = None,
        params: QueryParamTypes = None,
        headers: HeaderTypes = None,
        cookies: CookieTypes = None,
        auth: typing.Union[AuthTypes, UnsetType] = UNSET,
        allow_redirects: bool = True,
        timeout: typing.Union[TimeoutTypes, UnsetType] = UNSET,
    ) -> typing.Iterator[Response]


# 构造一个请求对象
httpx.Client.build_request(
        method: str,
        url: URLTypes,
        *,
        content: RequestContent = None,
        data: RequestData = None,
        files: RequestFiles = None,
        json: typing.Any = None,
        params: QueryParamTypes = None,
        headers: HeaderTypes = None,
        cookies: CookieTypes = None,
    ) -> httpx.Request


# 发出一个请求
httpx.Client.send(
        request: Request,
        *,
        stream: bool = False,
        auth: typing.Union[AuthTypes, UnsetType] = UNSET,
        allow_redirects: bool = True,
        timeout: typing.Union[TimeoutTypes, UnsetType] = UNSET,
    ) -> httpx.Response


# 关闭客户端(关闭传输和代理)
httpx.Client.close() -> None

httpx.Client 支持 with 语句:

with httpx.Client() as client:
    resp = client.get("https://httpbin.org/get")
    print(resp.text)

6.3 httpx.Client 请求示例

>>> import httpx
>>> 
>>> # 创建一个 HTTP 客户端
>>> client = httpx.Client(
...     headers={"User-Agent": "hello/world"},
...     http2=True
... )
>>> 
>>> # GET 请求
>>> resp = client.get("https://httpbin.org/get?aa=bb&cc=123",
...                   params={"dd": True, "xyz": "hello"})
>>> 
>>> print(resp.text)
{
  "args": {
    "aa": "bb", 
    "cc": "123", 
    "dd": "true", 
    "xyz": "hello"
  }, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Host": "httpbin.org", 
    "User-Agent": "hello/world"
  }, 
  "origin": "127.0.0.1", 
  "url": "https://httpbin.org/get?aa=bb&cc=123&dd=true&xyz=hello"
}
>>>
>>> # POST 请求
>>> resp = client.post("https://httpbin.org/post", content="Hello World")
>>> 
>>> print(resp.text)
{
  "args": {}, 
  "data": "Hello World", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "11", 
    "Host": "httpbin.org", 
    "User-Agent": "hello/world"
  }, 
  "json": null, 
  "origin": "127.0.0.1", 
  "url": "https://httpbin.org/post"
}
>>>
>>> # 提交 form 表单
>>> with open("aa.txt", "rb") as file1:
...     resp = client.post("https://httpbin.org/post",
...                       data={"key1": "value1"},
...                       files={"file_key1": file1}
...                       )
...     print(resp.text)
... 
{
  "args": {}, 
  "data": "", 
  "files": {
    "file_key1": "Text Content Demo"
  }, 
  "form": {
    "key1": "value1"
  }, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "281", 
    "Content-Type": "multipart/form-data; boundary=xxxxxx", 
    "Host": "httpbin.org", 
    "User-Agent": "hello/world", 
  }, 
  "json": null, 
  "origin": "127.0.0.1", 
  "url": "https://httpbin.org/post"
}
>>>
>>> # 关闭客户端
>>> client.close()

7. 异步 HTTP 客户端: httpx.AsyncClient

httpx.request()httpx.Client 相关方法都是同步请求方法,httpx 还支持 asyncio 异步请求,httpx 的异步请求使用 httpx.AsyncClient 异步 HTTP 客户端。

httpx.AsyncClienthttpx.Client 的异步版本,与之有相同的功能,同样支持 连接池保持连接HTTP/2Cookie 持久性 等功能,相关方法需使用 async/await 语法调用。

httpx.AsyncClient 类的构造方法:

# 方法参数与 httpx.Client 相同
class httpx.AsyncClient(
        *,
        auth: AuthTypes = None,
        params: QueryParamTypes = None,
        headers: HeaderTypes = None,
        cookies: CookieTypes = None,
        verify: VerifyTypes = True,
        cert: CertTypes = None,
        http2: bool = False,
        proxies: ProxiesTypes = None,
        mounts: typing.Mapping[str, AsyncBaseTransport] = None,
        timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
        limits: Limits = DEFAULT_LIMITS,
        max_redirects: int = DEFAULT_MAX_REDIRECTS,
        event_hooks: typing.Mapping[str, typing.List[typing.Callable]] = None,
        base_url: URLTypes = "",
        transport: AsyncBaseTransport = None,
        app: typing.Callable = None,
        trust_env: bool = True,
    )

httpx.AsyncClient 的方法和 httpx.Client 基本相同,不过前者的请求等相关会阻塞 IO 的方法为 async 异步方法:

class httpx.AsyncClient:
    async def get(...) -> Response          # GET 请求
    async def post(...) -> Response         # POST 请求
    async def put(...) -> Response          # PUT 请求
    async def patch(...) -> Response        # PATCH 请求
    async def head(...) -> Response         # HEAD 请求
    async def options(...) -> Response      # OPTIONS 请求
    async def delete(...) -> Response       # DELETE 请求
    
    async def request(...) -> Response      # 指定请求方法请求
    
    async def stream(...) -> AsyncIterator[Response]    # 流式请求
    
    def build_request(...) -> Request       # 构建请求(非异步方法)
    
    async def send(...) -> Request          # 发送请求
    
    async def aclose() -> None              # 关闭客户端
    
    async def __aenter__(...) -> self       # 异步上下文 进入方法
    async def __aexit__(...) -> None        # 异步上下文 退出方法

通过 httpx.AsyncClient 异步客户端发送请求返回的 Response 响应,需使用相关的 async 异步方法读取内容。

httpx.AsyncClient 代码示例:

import httpx
import asyncio

async def main():
    client = httpx.AsyncClient()
    resp = await client.get("https://httpbin.org/get")
    print(resp.text)        # 响应内容在 get() 返回响应之前已经是被(异步)读取完毕
    await client.aclose()   # 使用完毕后必须关闭客户端

asyncio.run(main())

httpx.AsyncClient 支持 ascyn with 语句:

import httpx
import asyncio

async def main():
    async with httpx.AsyncClient() as client:
        resp = await client.get("https://httpbin.org/get")
        print(resp.text)

asyncio.run(main())

httpx.AsyncClient 异步 GET、POST、流式请求 代码示例:

import httpx
import asyncio


async def main():
    client = httpx.AsyncClient()
    async with client:
        # GET 请求
        resp = await client.get("https://httpbin.org/get")
        print(resp.text)
        # 如果要迭代, 需使用 a 开头的异步方法异步迭代
        async for line in resp.aiter_lines():
            print(line)

        # POST 请求
        resp = await client.post("https://httpbin.org/post", content=b"Hello World")
        print(resp.text)

        # 提交 form 表单
        with open("data.txt") as file1:
            resp = await client.post("https://httpbin.org/post",
                                     data={"key1": "value1"},
                                     files={"file_key1": file1}
                                     )
            print(resp.text)

        # 流式请求 (一)
        async with client.stream("GET", "https://httpbin.org/get") as resp:
            # 需要使用 a 开头的方法异步迭代
            async for bs in resp.aiter_bytes(1024):
                print(bs)

        # 流式请求 (二)
        req = client.build_request("GET", "https://httpbin.org/get")
        resp = await client.send(req, stream=True)
        # 需要使用 a 开头的方法异步迭代
        async for line in resp.aiter_lines():
            print(line)


asyncio.run(main())