一. httpx 基础 HTTPX 是 Python 3 的全功能 HTTP 客户端,它提供同步和异步 API,并支持 HTTP/1.1 和 HTTP/2。
1.1 安装 1.2 快速开始 1.2.1 get请求 1 2 3 4 5 6 7 import httpxparams = { "wd" : "python" } resp = httpx.get("https://www.baidu.com/s" , params=params) print (resp.text)
1.2.2 post请求 表单 1 2 3 4 5 import httpxdata = {'key1' : 'value1' , 'key2' : 'value2' } r = httpx.post("https://httpbin.org/post" , data=data) print (r.text)
文件 1 2 3 4 5 6 7 import httpxfiles = {'upload-file' : open ('a.jpg' , 'rb' )} r = httpx.post("https://httpbin.org/post" , files=files) print (r.text)
JSON 1 2 3 4 5 import httpxdata = {'integer' : 123 , 'boolean' : True , 'list' : ['a' , 'b' , 'c' ]} r = httpx.post("https://httpbin.org/post" , json=data) print (r.text)
二进制 1 2 3 4 5 6 7 import httpxcontent = b'Hello, world' r = httpx.post("https://httpbin.org/post" , content=content, headers={ "Content-Type" : "application/octet-stream" , }) print (r.text)
Content-Type
在上传二进制数据时设置自定义标头
上传二进制数据时格式类型 常见的媒体格式类型:text/html : HTML格式 text/plain :纯文本格式 text/xml : XML格式 image/gif :gif图片格式 image/jpeg :jpg图片格式 image/png:png图片格式 以application开头的媒体格式类型:application/xhtml+xml :XHTML格式 application/xml: XML数据格式 application/atom+xml :Atom XML聚合格式 application/json: JSON数据格式 application/pdf:pdf格式 application/msword : Word文档格式 application/octet-stream : 二进制流数据(如常见的文件下载) application/x-www-form-urlencoded : <form encType="">
中默认的encType,form表单数据被编码为key/value格式发送到服务器(表单默认的提交数据的格式) 另外一种常见的媒体格式是上传文件之时使用的:multipart/form-data : 需要在表单中进行文件上传时,就需要使用该格式 1.2.3 响应处理 1 2 3 4 5 6 7 import httpxresp = httpx.request("GET" , "https://www.baidu.com" ) if resp.status_code == httpx.codes.OK: print (resp.text) print (resp.raise_for_status())
1.2.4 流式响应 对于大型下载,您可能希望使用不会一次将整个响应主体加载到内存中的流式响应。
1 2 3 4 5 6 7 8 9 import httpx with httpx.stream("GET" , "https://www.example.com" ) as r: for data in r.iter_bytes(): print (data)
注意 : 如果您以任何这些方式使用流式响应,则response.content
and response.text
属性将 不可用
1.2.5 cookie 1 2 3 4 5 6 7 8 9 10 11 12 13 import httpxr = httpx.get('https://httpbin.org/cookies/set?chocolate=chip' ) print (r.cookies['chocolate' ]) cookies_1 = {"peanut" : "butter" } cookies_2 = httpx.Cookies() cookies_2.set ('cookie_on_domain' , 'hello, there!' , domain='httpbin.org' ) cookies_2.set ('cookie_off_domain' , 'nope.' , domain='example.org' ) r = httpx.get('http://httpbin.org/cookies' , cookies=cookies_2) print (r.json())
1.2.6 重定向 默认情况下,HTTPX不会跟随所有 HTTP 方法的重定向,尽管这可以显式启用。 如,GitHub 将所有 HTTP 请求重定向到 HTTPS。
1 2 3 4 5 6 7 import httpxr = httpx.get('http://github.com/' ) print (r.status_code)print (r.history) print (r.next_request) resp = httpx.Client().send(r.next_request) print (resp.text)
follow_redirects=True : 跟随重定向
1 2 3 4 5 import httpxr = httpx.get('http://github.com/' , follow_redirects=True ) print (r.history) print (r.url) print (r.text)
1.2.7 超时和验证 接口请求默认超时为五秒, 可以通过 timeout 设置, timeout=None
表示禁用超时;
1 2 httpx.get('https://github.com/' , timeout=0.001 ) httpx.get('https://github.com/' , timeout=None )
要提供基本身份验证凭据,请将纯文本str或bytes对象的 2 元组作为auth参数传递给请求函数:
1 2 3 4 5 6 import httpxhttpx.get("https://example.com" , auth=("my_user" , "password123" )) auth = httpx.DigestAuth("my_user" , "password123" ) httpx.get("https://example.com" , auth=auth)
二、httpx 的两个坑 2.1 错误:httpx.ReadTimeout
实测发现,网络不稳定的情况下,极其容易出现该错误。 相对于 requests
库, httpx
库是有默认的超时时间的。 参考方案: 初始化时将 timeout
赋值为 None
1 2 3 client = httpx.AsyncClient(timeout=None ) 或者 httpx.get(url=url, timeout=None )
2.2 错误:SSL: CERTIFICATE_VERIFY_FAILED
在多域名或者通配符域名证书的情况下,可能会出现此类错误。 参考方案: 初始化时将 verify
赋值为 False
1 2 3 client = httpx.AsyncClient(verify=False ) 或者 httpx.get(url=url, verify=False )
2.3 综合 在引入 httpx 包 初始化时,将以上两个参数先行传入。
1 2 3 4 5 6 7 class NewClass : def __init__ (self ): self.client = httpx.AsyncClient(verify=False , timeout=None ) _header = { "User-Agent" : "Mozilla/5.0 (M1 Mac OS X 12) Safari/666.66" } self.client.headers.update(_header)
三、 客户端 3.1 特性 如果您来自 Requests,httpx.Client()
您可以使用它来代替 requests.Session()
.
1 2 3 4 5 6 7 8 9 10 with httpx.Client() as client: ... client = httpx.Client() try : ... finally : client.close()
3.2 发出请求 一旦有了,就可以使用,等Client发送请求。例如:.get() .post() ,其传递参数的方法都一样,要注意一点的是,在实例化Client的时候,可以传入请求参数,使得这个局部作用域内可以共享这些参数,跨请求共享配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 import httpx url = 'http://httpbin.org/headers' headers = {'user-agent' : 'my-app/0.0.1' } with httpx.Client(headers=headers) as client: r = client.get(url) print (r.json()['headers' ]['User-Agent' ]) headers = {'X-Auth' : 'from-client' } params = {'client_id' : 'client1' } with httpx.Client(headers=headers, params=params) as client: headers_ = {'X-Custom' : 'from-request' } params_ = {'request_id' : 'request1' } r = client.get('https://example.com' , headers=headers_, params=params_) print (r.request.url)print (r.request.headers['X-Auth' ])print (r.request.headers['X-Custom' ]) with httpx.Client(auth=('tom' , 'mot123' )) as client: r = client.get('https://example.com' , auth=('alice' , 'ecila123' )) _, _, auth = r.request.headers['Authorization' ].partition(' ' ) import base64 print (base64.b64decode(auth))
3.3、 其他配置 base_url
允许您为所有传出请求添加 URL
1 2 3 4 5 6 import httpx with httpx.Client(base_url='http://httpbin.org' ) as client: r = client.get('/headers' ) print (r.request.url)
3.4 python_web 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from flask import Flaskimport httpx app = Flask(__name__) @app.route("/" ) def hello (): return "Hello World!" with httpx.Client(app=app, base_url="http://localhost" ) as client: r = client.get("/" ) print (r.text) assert r.status_code == 200 assert r.text == "Hello World!"
3.5 Request对象 HTTPX 支持构建显式Request实例:
1 request = httpx.Request("GET" , "https://example.com" )
要将Request实例分派到网络,请创建一个Client实例并使用.send():
1 2 3 with httpx.Client() as client: response = client.send(request) ...
2.6 进度条 监控下载进度,使用响应流并检查 response.num_bytes_downloaded
属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import tempfileimport httpxfrom tqdm import tqdm with tempfile.NamedTemporaryFile() as download_file: url = "https://speed.hetzner.de/100MB.bin" with httpx.stream("GET" , url) as response: total = int (response.headers["Content-Length" ]) with tqdm(total=total, unit_scale=True , unit_divisor=1024 , unit="B" ) as progress: num_bytes_downloaded = response.num_bytes_downloaded for chunk in response.iter_bytes(): download_file.write(chunk) progress.update(response.num_bytes_downloaded - num_bytes_downloaded) num_bytes_downloaded = response.num_bytes_downloaded