Python爬虫-请求模块urllib3
urllib3是一个功能强大、条理清晰,用于HTTP 客户端的第三方模块,许多Python 的原生系统已经开始使用 urllib3。urllib3 提供了很多 Python 标准库里所没有的重要特性:
线程安全。
连接池。
客户端SSL/TLS验证。
使用 multipart编码上传文件。
Helpers用于重试请求并处理HTTP重定向。
支持gzip 和 deflate编码。
支持HTTP 和SOCKS 代理。
100%的测试覆盖率。
使用 urllib3 模块发送网络请求时,首先需要创建PoolManager对象,通过该对象调用request()方法来实现网络请求的发送。request()方法的语法格式如下:
request (method, url, fields = None, headers = None, urlopen_kw )
method:必选参数,用于指定请求方式,如GET、POST、PUT等。
url:必选参数,用于设置需要请求的URL地址。
fields:可选参数,用于设置请求参数。
headers:可选参数,用于设置请求头。
使用request()方法实现 GET请求
import urllib3 # 导入urllib3模块
url = "http://httpbin.org/get"
http = urllib3.PoolManager() # 创建连接池管理对象
r = http.request('GET',url) # 发送GET请求
print(r.status) # 打印请求状态码
运行:
200
使用PoolManager对象向多个服务器发送请求
一个PoolManager对象就是一个连接池管理对象,通过该对象可以实现向多个服务器发送请求。示例代码如下:
import urllib3 # 导入urllib3模块
urllib3.disable_warnings() # 关闭ssl警告
jingdong_url = 'https://www.jd.com/' # 京东url地址
python_url = 'https://www.python.org/' # Python url地址
baidu_url = 'https://www.baidu.com/' # 百度url地址
http = urllib3.PoolManager() # 创建连接池管理对象
r1 = http.request('GET',jingdong_url) # 向京东地址发送GET请求
r2 = http.request('GET',python_url) # 向python地址发送GET请求
r3 = http.request('GET',baidu_url) # 向百度地址发送GET请求
print('京东请求状态码:',r1.status)
print('python请求状态码:',r2.status)
print('百度请求状态码:',r3.status)
运行:
京东请求状态码: 200
python请求状态码: 200
百度请求状态码: 200
使用request()方法实现POST请求
使用urllib3 模块向服务器发送 POST 请求时并不复杂,与发送GET 请求相似,只是需要在 request()
方法中将 method参数设置为“POST”,然后将 fields参数设置为字典类型的表单参数。示例代码如下:
import urllib3 # 导入urllib3模块
urllib3.disable_warnings() # 关闭ssl警告
url = 'https://www.httpbin.org/post' # post请求测试地址
params = {'name':'Jack','country':'中国','age':30} # 定义字典类型的请求参数
http = urllib3.PoolManager() # 创建连接池管理对象
r = http.request('POST',url,fields=params) # 发送POST请求
print('返回结果:',r.data.decode('utf-8'))
运行:
返回结果: {
"args": {},
"data": "",
"files": {},
"form": {
"age": "30",
"country": "\u4e2d\u56fd",
"name": "Jack"
},
"headers": {
"Accept-Encoding": "identity",
"Content-Length": "307",
"Content-Type": "multipart/form-data; boundary=c9ed20638057b1b745b6f4c875f7609d",
"Host": "www.httpbin.org",
"User-Agent": "python-urllib3/2.0.7",
"X-Amzn-Trace-Id": "Root=1-66c039f0-2e8e92b95929522b717e3ec9"
},
"json": null,
"origin": "61.136.223.36",
"url": "https://www.httpbin.org/post"
}
通过retries参数设置重试请求
urllib3 模块可以自动重试请求,这种相同的机制还可以处理重定向。在默认情况下request()方法的请求重试次数为3次,如果需要修改重试次数可以设置 retries参数。修改重试测试的示例代码如下:
import urllib3 # 导入urllib3模块
urllib3.disable_warnings() # 关闭ssl警告
url = 'https://www.httpbin.org/get' # get请求测试地址
http = urllib3.PoolManager() # 创建连接池管理对象
r = http.request('GET',url) # 发送GET请求,默认重试请求
r1 = http.request('GET',url,retries=5) # 发送GET请求,设置5次重试请求
r2 = http.request('GET',url,retries=False) # 发送GET请求,关闭重试请求
print('默认重试请求次数:',r.retries.total)
print('设置重试请求次数:',r1.retries.total)
print('关闭重试请求次数:',r2.retries.total)
运行:
默认重试请求次数: 3
设置重试请求次数: 5
关闭重试请求次数: False
通过info()方法获取响应头信息
发送网络请求后,将返回一个HTTPResponse对象,通过该对象中的info()方法即可获取HTTP响应头信息,该信息为字典(dict)类型的数据,所以需要通过for循环进行遍历才可清晰的看到每条响应头信息内容。示例代码如下:
import urllib3 # 导入urllib3模块
urllib3.disable_warnings() # 关闭ssl警告
url = 'https://www.httpbin.org/get' # get请求测试地址
http = urllib3.PoolManager() # 创建连接池管理对象
r = http.request('GET',url) # 发送GET请求,默认重试请求
response_header = r.info() # 获取响应头
for key in response_header.keys(): # 循环遍历打印响应头信息
print(key,':',response_header.get(key))
运行:
Date : Sat, 17 Aug 2024 05:54:28 GMT
Content-Type : application/json
Content-Length : 286
Connection : keep-alive
Server : gunicorn/19.9.0
Access-Control-Allow-Origin : *
Access-Control-Allow-Credentials : true
处理服务器返回的JSON信息
如果服务器返回了一条JSON信息,而这条信息中只有某条数据为可用数据时,可以先将返回的JSON数据转换为字典(dict)数据,接着直接获取指定键所对应的值即可。示例代码如下:
import urllib3 # 导入urllib3模块
import json # 导入json模块
urllib3.disable_warnings() # 关闭ssl警告
url = 'https://www.httpbin.org/post' # post请求测试地址
params = {'name':'Jack','country':'中国','age':30} # 定义字典类型的请求参数
http = urllib3.PoolManager() # 创建连接池管理对象
r = http.request('POST',url,fields=params) # 发送POST请求
j = json.loads(r.data.decode('unicode_escape')) # 将响应数据转换为字典类型
print('数据类型:',type(j))
print('获取form对应的数据:',j.get('form'))
print('获取country对应的数据:',j.get('form').get('country'))
运行:
数据类型: <class 'dict'>
获取form对应的数据: {'age': '30', 'country': '中国', 'name': 'Jack'}
获取country对应的数据: 中国
处理服务器返回的二进制数据
如果响应数据为二进制数据,也可以做出相应的处理。例如,响应内容为图片的二进制数据时,则可以使用 open()函数,将二进制数据转换为图片。示例代码如下:
import urllib3 # 导入urllib3模块
urllib3.disable_warnings() # 关闭ssl警告
url = 'https://img-blog.csdnimg.cn/img_convert/a2386b5dadeda8ffc771ba4c8c0c0b2f.png' # 图片请求地址
http = urllib3.PoolManager() # 创建连接池管理对象
r = http.request('GET',url) # 发送网络请求
print(r.data) # 打印二进制数据
f = open('python.png','wb+') # 创建open对象
f.write(r.data) # 写入数据
f.close() # 关闭
运行:
xb5e\xed\xdeE@\x17#c\x12s\x9e7\xc0\x03Fp\xa0\xb2X\xf0\xb1R\xc8=\x0e\xa8\xe2\x84\xb3J.\x16\x80u\xb8fv\x07Y\xec\x94l#v\x86\x8d\x00\xbd\xfd\xb3O\x91\xb7O;SU\x16\x17\x9b\xe9#=X\xebl\xff\xec\x13\xe4\xed5\xeb\xc8^\xbbJZGi~\x9e5\x1e\x8c\xc8\xb5\xc6\x86x\x17\x07WB\x99 \xd0\xb3cY\xae\x0c\xed\xca\x90\xb57\t)\x1f:\x80\x08T\xff\x8a\x13w\xa20=\x8d\x01!\xce~\xe8\xef\xf8j\xd5\nka\x8e\xda\x0c\x1d\xe6\xe8\xe2`\x04n\xb4\x00\tu\xa5\x02g\xb6*\x
复杂请求的发送
设置请求头
设置请求头信息
请求头信息获取完成以后,将“User-Agent”设置为字典(dict)数据中的键,后面的数据设置为字典(dict)中 value。示例代码如下:
import urllib3 # 导入urllib3模块
urllib3.disable_warnings() # 关闭ssl警告
url = 'https://www.httpbin.org/get' # get请求测试地址
# 定义火狐浏览器请求头信息
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:77.0) Gecko/20100101 Firefox/77.0'}
http = urllib3.PoolManager() # 创建连接池管理对象
r = http.request('GET',url,headers=headers) # 发送GET请求
print(r.data.decode('utf-8')) # 打印返回内容
运行:
{
"args": {},
"headers": {
"Accept-Encoding": "identity",
"Host": "www.httpbin.org",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:77.0) Gecko/20100101 Firefox/77.0",
"X-Amzn-Trace-Id": "Root=1-66c03daa-35b3202d795b1efd273ffb61"
},
"origin": "61.136.223.36",
"url": "https://www.httpbin.org/get"
}
设置超时
在没有特殊要求的情况下,可以将设置超时的参数与时间填写在 request()方法或者是 PoolManager()实例对象中,示例代码如下:
import urllib3 # 导入urllib3模块
urllib3.disable_warnings() # 关闭ssl警告
baidu_url = 'https://www.baidu.com/' # 百度超时请求测试地址
python_url = 'https://www.python.org/' # Python超时请求测试地址
http = urllib3.PoolManager() # 创建连接池管理对象
try:
r = http.request('GET',baidu_url,timeout=0.01)# 发送GET请求,并设置超时时间为0.01秒
except Exception as error:
print('百度超时:',error)
http2 = urllib3.PoolManager(timeout=0.1) # 创建连接池管理对象,并设置超时时间为0.1秒
try:
r = http2.request('GET', python_url) # 发送GET请求
except Exception as error:
print('Python超时:',error)
运行:
百度超时: HTTPSConnectionPool(host='www.baidu.com', port=443): Max retries exceeded with url: / (Caused by ConnectTimeoutError(<urllib3.connection.HTTPSConnection object at 0x000002961EBF5BC8>, 'Connection to www.baidu.com timed out. (connect timeout=0.01)'))
Python超时: HTTPSConnectionPool(host='www.python.org', port=443): Max retries exceeded with url: / (Caused by ConnectTimeoutError(<urllib3.connection.HTTPSConnection object at 0x000002961EC07308>, 'Connection to www.python.org timed out. (connect timeout=0.1)'))
上传文件
通过指定fields参数上传文本文件
import urllib3 # 导入urllib3模块
import json # 导入json模块
with open('test.txt') as f: # 打开文本文件
data = f.read() # 读取文件
http = urllib3.PoolManager() # 创建连接池管理对象
# 发送网络请求
r = http.request( 'POST','http://httpbin.org/post',fields={'filefield': ('example.txt', data),})
files = json.loads(r.data.decode('utf-8'))['files'] # 获取上传文件内容
print(files) # 打印上传文本信息
运行:
{'filefield': '在学习中寻找快乐!'}
通过指定body参数上传图片文件
import urllib3 # 导入urllib3模块
with open('python.jpg','rb') as f: # 打开图片文件
data = f.read() # 读取文件
http = urllib3.PoolManager() # 创建连接池管理对象
# 发送请求
r = http.request('POST','http://httpbin.org/post',body = data,headers={'Content-Type':'image/jpeg'})
print(r.data.decode()) # 打印返回结果
运行:
{
"args": {},
"data": "data:application/octet-stream;base64,iVBORw0KGgoAAAANSUhEUgAAALEAAABACAYAAABV55Z5AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsIAAA7CARUoSoAAABkjSURBVHhe7V0JnBTF9f7m