小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
本文已参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金
简介
HTTPX 是最近 GitHub看的到一个比较火的一个项目,根据官网的描述,总结有如下特点:
- 和使用 requests 一样方便,requests 有的它都有
- 加入 HTTP/1.1 和 HTTP/2 的支持。
- 能够直接向 WSGI 应用程序或 ASGI 应用程序发出请求。
- 到处都有严格的超时设置
- 全类型注释
- 100% 的测试覆盖率
比较不错的一个特点是全类型注解,这让我想起了一个叫 Starlette 的库,它也是全类型注解的,类型注解主要方便IDE的智能提示,Java 等静态类型的语言都有这个功能,Python 是近期新加的。其他的后面再说吧,我们还是看例子吧。
安装
httpx 的安装很简单,像其他的 Python 库一样,直接 pip 就完事了
python3 -m pip install httpx
如果需要对 HTTP/2 支持,我们需要额外安装一个库
python3 -m pip install httpx[http2]
使用示例
import httpx
r = httpx.get('https://www.example.org/')
r.text
r.content
r.json()
r.status_code
基本的用法直接导包然后 get 就行了。其他的和 requests 的使用类似
r = httpx.put('https://httpbin.org/put', data={'key': 'value'})
r = httpx.delete('https://httpbin.org/delete')
r = httpx.head('https://httpbin.org/get')
r = httpx.options('https://httpbin.org/get')
Ok,这是基本用法。
一个Http/2的使用例子
import httpx
client = httpx.Client(http2=True, verify=False)
headers = {
'Host': 'pixabay.com',
'upgrade-insecure-requests': '1',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36',
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'sec-fetch-site': 'none',
'sec-fetch-mode': 'navigate',
'sec-fetch-dest': 'document',
'accept-language': 'zh-CN,zh;q=0.9'
}
response = client.get('https://pixabay.com/images/search/cat/', headers=headers)
print(response.text)
如果直接使用requests的话,会返回403的错误页面。
如果需要做一个爬虫项目,里面涉及到 Cookie 的传递这时候再这样就不行了,httpx 有个 requests 的 Session 类型的使用方法.
import httpx
client = httpx.Client() #类似requests.Session()
try:
do somting
finally:
client.close() #关闭连接池
更优雅的方法就是使用 with 上下文管理器的形式
with httpx.Client() as client:
headers = {'X-Custom': 'value'}
r = client.get('https://example.com', headers=headers)
这里有个地方需要强调下 Client 和 get 里面都可以添加 headers, 最后这两个地方的 headers 可以合并到请求里,官方的例子
>>> 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)
...
>>> r.request.url
URL('https://example.com?client_id=client1&request_id=request1')
>>> r.request.headers['X-Auth']
'from-client'
>>> r.request.headers['X-Custom']
'from-request'
接下来说下大家比较关心的一点代理的使用,需要注意的是 httpx 的代理只能在 httpx.Client 创建 Client 的实例的时候使用,client.get 的时候没这个参数。 有意思的是它这个代理可以指定规则,限制哪些请求使用代理哪些不使用,来个官方的例子
允许所有请求都走代理
proxies = {
"all://": "http://localhost:8030",
}
如果字典的值为 None 则表示不使用代理。
不同的协议走不用的代理
proxies = {
"http://": "http://localhost:8030",
"https://": "http://localhost:8031",
}
http 走 8030 的代理,https 走 8031 的代理,这里要注意和用 requests 使用代理的区别 requests 是下面这样用的
proxies = {
"http": "http://localhost:8030",
"https": "http://localhost:8030",
}
综合使用
你还可以配置多个规则像下面这
proxies = {
# Route all traffic through a proxy by default...
"all://": "http://localhost:8030",
# But don't use proxies for HTTPS requests to "domain.io"...
"https://domain.io": None,
# And use another proxy for requests to "example.com" and its subdomains...
"all://*example.com": "http://localhost:8031",
# And yet another proxy if HTTP is used,
# and the "internal" subdomain on port 5550 is requested...
"http://internal.example.com:5550": "http://localhost:8032",
}
代理就这些,下面看看它的链接池的问题 你可以使用 Client 的关键字参数 limits 来控制连接池的大小。它需要以下实例httpx.Limits 来定义:
- max_keepalive,允许的保持活动连接数或 None 始终允许。(预设10)
- max_connections,允许的最大连接数或 None 无限制。(默认为100)
limits = httpx.Limits(max_keepalive_connections=5, max_connections=10)
client = httpx.Client(limits=limits)
如果默认链接数不够用的就自己重新设置吧。(我感觉是不够
我这边只关注了爬虫可能用到的部分,其他的大家可以看看官网。比如怎么搭配flask使用等。
好了关于httpx的同步请求的内容大概就这些要提的。如果只讲到这里,你肯定会说,"就这,用 requests 不香么?",emmm,如果这么想你就错了,要知道它不仅支持同步还支持异步的(手动滑稽),使用起来比 aiohttp 简单多了,这才是我推荐的目的。
httpx 之异步请求
要知道官网可是单独把它拎出一节讲的,可以看出里面应该有点东西。 废话少说,开整。
我们先看在 aiohttp 中是如何创建并发送请求的
import aiohttp
import asyncio
async def main():
async with aiohttp.ClientSession() as client:
async with client.get('http://httpbin.org/get') as resp:
assert resp.status == 200
html= await resp.text()
print(html)
我们需要使用两个 async with 来完成一个请求,然后我们看看 httpx 怎么实现的呢
async with httpx.AsyncClient() as client:
resp = await client.get('http://httpbin.org/get')
assert resp.status_code == 200
html = resp.text
如果写框架封装模块的时候可以这么使用
from dataclasses import dataclass
@dataclass
class HTTPClient:
def __post_init__(self):
self.spider_config = SpiderConfig
proxies = {
"all://": None,
}
if self.spider_config.get("USE_PROXY"):
from config.proxy_config import PROXY_SERVER
proxies = PROXY_SERVER
self.session = httpx.AsyncClient(proxies=proxies)
async def close(self):
crawler.info("close session")
return await self.session.aclose()
async def __aenter__(self) -> "HTTPClient":
return self
async def __aexit__(self,
exc_type: Optional[Type[BaseException]],
exc_val: Optional[BaseException],
exc_tb: Optional[TracebackType], ) -> Optional[bool]:
await self.close()
return None
\
感觉总体上比较 aiohttp 写起来舒服多了,少写很多异步代码。 之前使用 aiohttp 中的 resp.status 来获取状态码的时候写了status_code,应该是使用 requests 习惯了吧,这下好了使用 httpx 不用担心这个写错的问题了。
和aiohttp对比
网上找到别人测试的结果,大家也可以自己测试下
后记
最近,我刚把我之前的那个 discogs_aio_spider 的项目给改了,之前用的aiohttp,我现在改成 httpx,对 httpx 感兴趣的朋友,可以到上面研究下我这个项目,有问题欢迎提出。👏 项目名:discogs_aio_spider 项目地址:github.com/cxapython/d… 使用到的模块:asyncio、httpx、motor、aio-pika、aioredis
官网地址:HTTPX