前言:
而今姐妹们对“cookielibpython3”大致比较讲究,看官们都想要知道一些“cookielibpython3”的相关资讯。那么小编也在网上收集了一些对于“cookielibpython3””的相关资讯,希望朋友们能喜欢,小伙伴们一起来了解一下吧!这是一篇译文,原文地址:
欢迎关注我的公众号:ReadingPython
Python 社区有很多 HTTP 客户端,在 GitHub 上搜“Python HTTP Clients”可以返回 1700 多个结果。
本文主要讨论五种目前可用的、最好的 HTTP 客户端,分析它们的优劣区别。
背景
作为例子,我们将向星球大战API(swapi.dev)发送 GET 请求并解析返回的 JSON 数据。返回数据示例如下:
{ "name": "Death Star", "model": "DS-1 Orbital Battle Station", "manufacturer": "Imperial Department of Military Research, Sienar Fleet Systems", "cost_in_credits": "1000000000000", ...}
作为 POST 请求的例子,我们会将下面的数据发送至 httpbin.org:
{ "name": "Obi-Wan Kenobi", "height": "182", "mass": "77", "hair_color": "auburn, white", "skin_color": "fair", "eye_color": "blue-gray", "birth_year": "57BBY", "gender": "male"}0. 标准库
熟悉 Python 标准库的人可能也知道 urllib 库和 urllib2 库的纠葛历史。Python3 中,最初的 urllib2 库被一分为二,urllib.request 和 urllib.error。
我们先来看看,如何只用标准库发送 HTTP 请求。
import jsonimport urllib.requestresponse = urllib.request.urlopen(';)text = response.read()print(json.loads(text.decode('utf-8')))
注意,urllib.request 返回的是二进制数据,我们需要手动调用 json 库将其转换为可读数据。
POST 请求的代码如下:
import jsonfrom urllib import request, parsedata = {"name": "Obi-Wan Kenobi", ...}encoded_data = json.dumps(data).encode()req = request.Request(';, data=encoded_data)req.add_header('Content-Type', 'application/json')response = request.urlopen(req)text = response.read()print(json.loads(text.decode('utf-8')))
提交数据时,也需要手动执行 json 数据编码,并设置“Content-Type”头。
你可能会觉得这些操作比较麻烦——很多其它开发者也是这么认为的,这也是社区出现这么多 HTTP 客户端的原因。接下来,我们会介绍其中最好的 5 个。
1. urllib3
urllib3 是一个强力、易用的 HTTP 客户端,用户很多,它提供了很多标准库缺失的重要特性。
很奇怪,基于 urllib 的 urllib3 并不是标准库的一部分。它提供了很多重要特性,如连接池、TLS 支持以及线程安全等。urllib3 实现了连接复用,在爬虫等领域的性能要比标准库好很多。
urllib3 是我们后面要讨论的 requests 包的依赖项,每月下载量高达 1500 万次。使用 urllib3 发起 HTTP 请求的代码如下:
import urllib3import jsonhttp = urllib3.PoolManager()r = http.request('GET', ';)print(json.loads(r.data.decode('utf-8')))
和标准库一样,我们还是得手动执行 JSON 格式转换。
POST 请求如下:
import jsonimport urllib3data = {"name": "Obi-Wan Kenobi", ...}http = urllib3.PoolManager()encoded_data = json.dumps(data).encode('utf-8')r = http.request( 'POST', ';, body=encoded_data, headers={'Content-Type': 'application/json'})print(json.loads(r.data.decode('utf-8')))
Poolmanager 对象负责连接池管理,保证线程安全,而 HTTP 方法只是传入请求函数的一个字符串参数。urllib3 的许多特性都是通过 Poolmanager 实现的。缓存的连接池保证我们在请求同一个服务器时可以复用 HTTP 连接。如果我们需要对同一个服务器发起很多请求,可以增大连接池的容量。
urllib3 支持复杂的重试策略。这个特性也很重要——在访问一些繁忙的服务器时,万一超时还可以多试几次。相关细节可以阅读 urllib3 的文档。
不过,使用连接池也带来了一个问题,就是 cookie 不好管理,只适合无状态的 HTTP 请求。有时必须手动设置 cookie:
headers={'Cookie': 'foo=bar; hello=world'}
鉴于很多其它库都依赖 urllib3,我想这个包大概还会存续很长一段时间。
2. Requests
Requests 是一个简洁优雅的 HTTP 包。
requests 包在 Python 社区广受好评,根据 PePy 的数据,每月下载量高达 1100 万次。在 urllib.request 的官方文档中,也推荐大家使用 requests 作为“高阶 HTTP 客户端”。这个库的使用极其简单,大多数 Python 开发者都将其作为默认选择。
requests 由 Python 软件基金会维护,在 GitHub 上有 45k 颗星,同时也是众多其它 Python 库,如 gRPC、pandas 等的依赖项。
我们还是来复习一下,用 requests 发起 HTTP 请求:
import requestsr = requests.get(';)print(r.json())
POST 请求也很简单,只需修改调用的方法即可:
import requestsdata = {"name": "Obi-Wan Kenobi", ...}r = requests.post(';, json=data)print(r.json())
可以理解 requests 为什么这么受欢迎了吧——它的设计实在太优雅了!使用最少的代码,直接调用对应的 HTTP 方法(GET/POST),不需要手动执行 JSON 数据编解码。对开发者来说,即好用又好懂。
发送 POST 请求时,也不需要操心数据编码、设置 HTTP 头之类的事情,一切都由 requests 自动完成。
如果要提交的是表单,将 "json" 字段替换成 "data" 即可。
设置 cookie 的方法也很简单,代码如下:
r = requests.post(';, data=data, cookies={'foo': 'bar', 'hello': 'world'}))
同时,requests 还支持 session、请求钩子、自定义重试策略等。使用 session 时,可以在不同请求之间共享 cookie,实现有状态的 HTTP 访问,这正是 urllib3 不支持的特性。下面是一个官方文档中的例子:
s = requests.Session()s.get(';)r = s.get(';)print(r.text)# '{"cookies": {"sessioncookie": "123456789"}}'
而请求钩子可以在每次请求之后都执行同样的动作。大家可能对 git 中的类似概念比较熟悉。更多特性可以参考官方文档。
对大多数应用来说,requests 都是一个不错的选择。
3. AIOHTTP
AIOHTTP 是一个异步 HTTP 客户端/服务器。
AIOHTTP 既有客户端也有服务器,适用于既提供 API 又请求别人的 API 的场景,在 GitHub 上有 11k 颗星,也是很多第三方库的依赖项。
使用 AIOHTTP 发送一个 GET 请求的代码如下:
import aiohttpimport asyncioasync def main(): async with aiohttp.ClientSession() as session: async with session.get(';) as response: print(await response.json())loop = asyncio.get_event_loop()loop.run_until_complete(main())
POST 请求的代码如下:
import aiohttpimport asynciodata = {"name": "Obi-Wan Kenobi", ...}async def main(): async with aiohttp.ClientSession() as session: async with session.post(';, json=data) as response: print(await response.json())loop = asyncio.get_event_loop()loop.run_until_complete(main())
可以看到,请求方法与 requests 类似,但整体代码长了很多,引入了 asyncio 包并用 async 与 await 调用请求方法。
关于与 requests 等其它库的区别,官方文档做了很好的说明。如果你不熟悉异步概念的话可能得花点时间理解一下。简单地说就是,我们可以同时发起多个请求,而不用等前一个请求响应之后才发起另一个请求。如果只需要请求一次的话,这个问题无关紧要,但在数十个乃至百千万个请求场景下,让 CPU 等待显然是不合适的。
例如,我们可以查询星球大战 API 中的前 50 艘战舰:
import aiohttpimport asyncioimport timeasync def get_starship(ship_id: int): async with aiohttp.ClientSession() as session: async with session.get(f'{ship_id}/') as response: print(await response.json())async def main(): tasks = [] for ship_id in range(1, 50): tasks.append(get_starship(ship_id)) await asyncio.gather(*tasks)asyncio.run(main())
这些代码在我电脑上的运行时间稳定在 2 秒左右,而用 requests 库实现则需要 4 秒。因此,异步请求可以大幅提高我们获取数据的效率。
关于 AIOHTTP 支持的其它特性,如 session、cookie、连接池、dns 缓存、客户端追踪等,官方文档都有完备说明。不过,它并不支持复杂的请求重试策略,必须引入其它第三方包来实现这个功能。
4. GRequests
GRequests 使用 Gevent 实现异步请求,Gevent 是一个“基于协程的网络库”。这个包出来得比较早,第一个 release 是在 2012 年,那时 Python 标准库中还没有 asyncio 包呢。
通过这个包,我们可以发起单次请求,也可以异步发起多个请求:
import grequestsreqs = []for ship_id in range(0, 50): reqs.append(grequests.get(f'{ship_id}/'))for r in grequests.map(reqs): print(r.json())
GRequests 的文档比较少,甚至在自己的 Github 页面上也推荐大家使用其它包。毕竟它只有 165 行代码,并没有在 requests 的基础上提供更多特性。在过去 9 年中,GRequests 只发布了 6 个 release 版本,因此,如果不是真的搞不懂异步编程的话,不是很建议使用这个包。
5. HTTPX
HTTPX 是这几个包里比较年轻的(第一个 release 在 2015 年发布),目前还在 beta 版本,正式版预计 2021 年会出来。它支持“大体与 requests 包对应的 API”,也是这几个包中唯一同时支持 HTTP2 和异步请求的。
HTTPX 的用法与 requests 类似:
import httpxr = httpx.get(';)print(r.json())
POST 请求代码如下:
import httpxdata = {"name": "Obi-Wan Kenobi", ...}r = httpx.post(';, json=data)print(r.json())
在上面的代码中,我们只是简单地改了下包名。值得注意的是,HTTPX 支持异步,但这些代码都是按同步方式写的。
通过 http.AsyncClient 对象,我们也可以进行异步调用。例如,可以更新前面的 get_starship 方法如下:
import httpximport asyncioasync def get_starship(ship_id: int): async with httpx.AsyncClient() as client: r = await client.get(f'{ship_id}/') print(r.json())...
在需要多次请求的场景中,建议使用异步方式。
如果你需要重构 requests 写的脚本,使之支持异步的话,推荐使用 HTTPX 作为替换选项。
不同客户端的比较
下面的基准测试值仅供参考,由于所处区域、网络状况的不同,大家测试的结果可能有所区别。测试时,非异步请求使用 timeit 包记录执行 50 次请求的时间并计算平均值,异步请求使用 time 包记录时长并计算平均值。由于需要比较 GET 和 POST 请求的数据,这些请求都发送至 httpbin.org。不同包的下载数据来自 PePy。
可以看到,仅从单次请求的性能上看,urllib3 有优势——POST请求所花时间比其它包短了很多。而单次 GET 请求的时间大家都差不多。
有趣的是,虽然 urllib3 在 GitHub 上的星星数最少(星星数一般是受欢迎度的表现),它的实际下载量却是最多的(还记得吗?前文提到,它是 requests 的依赖项)。而 requests 无疑是最受欢迎的包,它提供的很多特性并不在上面基准测试数据的表现范围内。
异步包中,AIOHTTP 表现最好,单次 GET 请求与 POST 请求所花的平均时间最短,同时星星数与下载数也最多。不过这也可能是因为它同时提供 HTTP 服务器的缘故。
鉴于 GRequets 比较低的活跃度以及它在自己页面上的建议,除非你有很特殊的需求,否则不建议使用这个包。
总结
综上,我们看到,requests 库影响了很多其它库的设计,在社区中广受欢迎,是大多数开发者的默认选择,同时也支持 session 和重试等特性,在需求不复杂时,或许可以作为你的首选项。
对需求较复杂,需要同时发起许多请求的开发者来说,AIOHTTP 是目前的最佳选择。它在基准测试中的异步表现最好,下载量与星星数最多,并能提供稳定版本。但是它也比较复杂,不支持重试策略,因此,如果你不在意 beta 版本,可以选择 HTTPX。
不论何种场景,总是能找到适用于你的 Python HTTP 客户端的。
ReadingPython:编程技术相关,优质内容翻译与阅读分享为主
标签: #cookielibpython3