前面我们学习了urllib库和requests库的使用,已经可以爬取大多数网站的数据了,但是对于一些网站还是无能为力,究其原因是因为这些网站前置使用HTTP/2.0协议访问,这时urllib和requests是无法访问数据的,因为它们支支持HTTP/1.1,不支持HTTP2.0。那么这时该怎么办呢?
还是有办法的只需要使用一些支持HTTP/2.0请求库就可以了,requests已有的功能他都支持。
所以本节就开始httpx的使用。
引入
下面我们来看一个实例,https://spa16.scrape.center/就是一个强制HTTP/2.0访问的一个网站,用浏览器打开此网站,f12点击网络,右键点击表头选择协议,可以看到协议一列都是h2,证明请求所用的协议是HTTP/2.0。
这个网站是无法使用requests爬取的,我们可以试一下
import requests
url ='https://spa16.scrape.center/'
resp = requests.get(url)
print(resp.text)
运行结果报错:
File "d:\跟着崔庆才学爬虫\.venv\Lib\site-packages\urllib3\connectionpool.py", line 791, in urlopen
response = self._make_request(
..........
File "C:\Users\30255\AppData\Local\Programs\Python\Python311\Lib\http\client.py", line 287, in _read_status
raise RemoteDisconnected("Remote end closed connection without"
http.client.RemoteDisconnected: Remote end closed connection without responseDuring handling of the above exception, another exception occurred:
Traceback (most recent call last):
.......
File "C:\Users\30255\AppData\Local\Programs\Python\Python311\Lib\http\client.py", line 287, in _read_status
raise RemoteDisconnected("Remote end closed connection without"
urllib3.exceptions.ProtocolError: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))During handling of the above exception, another exception occurred:
Traceback (most recent call last):
可以看到首先抛出的错误就是RemoteDisconnected错误,请求失败。
可能有人会认为这是没有设置请求头导致的,其实不是,真实原因是requests这个库是使用HTTP/1,1访问的目标网站,而目标网站会检测请求使用的是不是HTTP/2.0如果不是就拒绝返回任何结果。
安装
安装命令 : pip install httpx
但是这样安装完的httpx是不支持HTTP/2.0的,如果想支持可以这样安装
pip install 'httpx[http2]'
这样就既安装了httpx,又安装了httpx对http/2.0的支持模块
基本使用
用之前的测试网站看一下httpx的基本使用
import httpx
resp = httpx.get('https://www.httpbin.org/get')
print(resp.status_code)
print(resp.headers)
print(resp.text)
运行结果如下
200
Headers({'date': 'Thu, 16 Nov 2023 07:23:51 GMT', 'content-type': 'application/json', 'content-length': '312', 'connection': 'keep-alive', 'server': 'gunicorn/19.9.0', 'access-control-allow-origin': '*', 'access-control-allow-credentials': 'true'})
{
"args": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Host": "www.httpbin.org",
"User-Agent": "python-httpx/0.25.1",
"X-Amzn-Trace-Id": "Root=1-6555c387-1a52f7803da99c9f5716c911"
},
"origin": "112.32.67.234",
"url": "https://www.httpbin.org/get"
}
这里可以看出httpx和requests的用法基本一致,将返回结果赋值为resp变量,然后打印状态码,请求头,以及文本等属性,通过返回结果我们可以看到其中的User-Agent是一个 "User-Agent": "python-httpx/0.25.1",代表我们用的是httpx请求的
我们更换一下User-Agent在请求一次,代码如下
import httpx
from fake_useragent import UserAgent
headers = {"User-Agent":UserAgent().chrome}
resp = httpx.get('https://www.httpbin.org/get',headers=headers)
print(resp.text)
请求结果如下:
{
"args": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Host": "www.httpbin.org",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36",
"X-Amzn-Trace-Id": "Root=1-6555c5a4-416117406c4a2c030ef6bab3"
},
"origin": "112.32.67.234",
"url": "https://www.httpbin.org/get"
}
可以发现,我们更换的User-Agent生效了。
现在我们再用开头的实例网站,测试一下httpx是否能够爬取网站内容
import httpx
url ='https://spa16.scrape.center/'
resp = httpx.get(url)
print(resp.text)
运行发现抛出了和requests请求时类似的错误,不是支持HTTP/2.0吗?httpx默认是不会开启对HTTP/2.0的支持的,默认使用的时HTTP/1.1,需要手动开启才能使用HTTP/2.0
改写代码如下
import httpx
client = httpx.Client(http2=True)
url ='https://spa16.scrape.center/'
resp = client.get(url)
print(resp.text)
运行结果如下:
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><meta name=referrer content=no-referrer><link rel=icon href=/favicon.ico><title>Scrape | Book</title><link href=/css/chunk-50522e84.e4e1dae6.css rel=prefetch><link href=/css/chunk-f52d396c.4f574d24.css rel=prefetch><link href=/js/chunk-50522e84.6b3e24aa.js rel=prefetch><link href=/js/chunk-f52d396c.f8f41620.js rel=prefetch><link href=/css/app.ea9d802a.css rel=preload as=style><link href=/js/app.b93891e2.js rel=preload as=script><link href=/js/chunk-vendors.a02ff921.js rel=preload as=script><link href=/css/app.ea9d802a.css rel=stylesheet></head><body><noscript><strong>We're sorry but portal doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=/js/chunk-vendors.a02ff921.js></script><script src=/js/app.b93891e2.js></script></body></html>
这里需要声明一个Client对象,赋值为client变量,同时显式的将http2参数设置为True,这样便开启了对HTTP/2.0的支持,运行发现成功获取到HTML代码了。这也就印证了这个示例网站只能使用HTTP/2.0访问。
上面提到,httpx和requests有很多相似的api,上面实现的是get请求,对于POST请求,PUT请求和DELETE请求来说,实现方式是类似的
import httpx
r = httpx.get('https://www.httpbin.org/get')
r = httpx.post('https://www.httpbin.org/post')
r = httpx.put('https://www.httpbin.org/put')
r = httpx.delete('https://www.httpbin.org/delete')
基于得到的resp对象,可以使用如下属性和方法获得想要的内容。
status_code:状态码
text:响应体的文本内容
content:响应体的二进制内容如图片等。
header:响应头,是Headers对象,可以用像获取字典中的内容一样获取其中某个header的值
json:方法,可以调用此方法将文本转化为json对象。
此外还有一些基本用法基本都与requests类似,具体可以参考官方文档
https://www.python-httpx.org/quickstart
Client对象
上面说到httpx和requests对象有很多相似的部分,那么现在就来聊聊不同的部分,首先说到的就是Client对象,这个对象可以和requests中session对象类比学习。
下面我们就来介绍Client对象的使用,官方比较推荐的使用方式是with as语句,实例如下
import httpx
with httpx.Client() as client:
resp=client.get('https://www.httpbin.org/get')
print(resp)
运行结果:<Response [200 OK]>
这个方法等价于
import httpx
client= httpx.Client()
try:
resp=client.get('https://www.httpbin.org/get')
finally:
print(resp)
client.close()
两种方法运行结果是一样的。只不过这里需要我们在最后显式地调用了close方法来关闭client对象。
另外,在声明Client对象时可以指定一些参数,例如headers,这样使用该对象发起的所有请求都会默认带上这些参数配置。
示例如下
import httpx
url = 'https://www.httpbin.org/headers'
headers={"User-Agent":"my-request/0.0.1"}
with httpx.Client(headers=headers)as client:
r=client.get(url)
print(r.json()['headers']['User-Agent'])
这里我们声明了一个headers对象,内容为User-Agent属性,然后将此变量传递给headers参数初始化了一个Client对象,并赋值为client变量,最后用client变量请求了测试网站。并打印了返回结果中的User-Agent的内容:
my-request/0.0.1
可以看到成功赋值了
关于更多client对象的高级用法可以参考官网文档:https://www.python-httpx.org/advanced
支持HTTP/2.0
现在是要在客户端开启对HTTP/2.0的支持,就像基本使用小节所说的那样,同样是声明Client对象,然后将http2的参数设置为True。如果不设置,那么请求就会默认支持HTTP/1.0,即不开启对HTTP/2.0的支持。写法如下
import httpx
client = httpx.Client(http2=True)
resp = client.get('https://www.httpbin.org/get')
print(resp.text)
print(resp.http_version)
这里我们在输出代码上加了一个http_version属性,这是requests库中不存在的属性,其结果可能为:HTTP/1.0,HTTP/1.1,HTTP/2.0
代码运行的返回结果为 HTTP/2.0,代表使用了HTTP/2.0协议传输。
注意。在客户端的httpx启用了对HTTP/2.0的支持并不意味着请求和响应都将通过HTTP/2.0传输,这里得客户端和服务端都支持HTTP/2.0才行。如果客户端连接到仅支持HTTP/1.1的服务器,那么它也需要改用HTTP/1.1
支持异步请求
httpx还支持异步客户端请求(即AsyncClient),支持Python的async的请求模式,写法如下:
import httpx
import asyncio
async def fetch(url):
async with httpx.Client(http2=True) as client:
resp=await client.get(url)
print(resp.text)
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(fetch('https://www.httpbin.org/get'))
关于异步请求,目前仅了解即可,后面章节会专门对异步请求进行学习,也可以参考官网文档
https://www.python-httpx.org/async
总结
本章介绍了httpx的基本用法,该库的API与requests的非常相似,简单易用,同时支持HTTP/2.0,如果后面有需要requests爬取网页时,推荐大家使用httpx