Python标准库:HTTP客户端库urllib3

urllib3功能强大且易于使用,用于HTTP客户端的Python库。许多Python的原生系统已经开始使用urllib3。

urllib3提供了很多python标准库urllib里所没有的重要特性:

  • 线程安全
  • 连接池
  • 客户端SSL/TLS验证
  • 文件分部编码上传
  • 协助处理重复请求和HTTP重定位
  • 支持gzip和deflate压缩编码
  • 支持HTTP和SOCKS代理
  • 100%测试覆盖率

Python3.x中将urllib2合并到了urllib,之后此包分成了以下几个模块:

  1. urllib.<strong>request:</strong> 用于打开和读取URL
  2. urllib.<strong>error</strong>: 用于处理前面request引起的异常
  3. urllib.<strong>parse:</strong> 用于解析URL
  4. urllib.<strong>robotparser:</strong>用于解析robots.txt文件

一、安装

urllib3是一个第三方库,pip安装:

pip install urllib3

二、urllib.parse的使用

1、urlparse:  将urlstr解析成各个组件

import urllib.parse

url = "http://www.baidu.com"
parsed = urllib.parse.urlparse(url)
print(parsed)
# 输出:ParseResult(scheme=‘http‘, netloc=‘www.baidu.com‘, path=‘‘, params=‘‘, query=‘‘, fragment=‘‘)

2、urljoin(baseurl,newurl,allowFrag=None):  将url的根域名和新url拼合成一个完整的url

import urllib.parse

url = "http://www.baidu.com"
new_path = urllib.parse.urljoin(url, "index.html")
print(new_path)
# 输出:http://www.baidu.com/index.html

3、urlencode():  将dict中的键值对以连接符&划分,即将字典类型的请求数据转变为url编码

import urllib.parse
dic = {‘name‘:‘melon‘,‘age‘:18}
data = urllib.parse.urlencode(dic)

print(data)     #age=18&name=melon

urllib.parse.quote(url):(URL编码处理)主要对URL中的非ASCII码编码处理

urllib.parse.unquote(url):(URL解码处理)URL上的特殊字符还原

三、urllib.request 的使用

1、urlretrieve(url,filename,reporthook,data):  下载url定位到的html文件。

不写路径filename则会被存为临时文件,可以用 urllib.urlcleanup() 来清理缓存

file_name = urllib.request.urlretrieve(‘http://www.baidu.com‘,‘%s/baidu.html‘%BASE_DIR)

2、urlopen(url,data,timeout):打开一个url的方法,返回一个文件对象,然后可以进行类似文件对象的操作

模块中最常用的函数为urllib.request.urlopen()

urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)

参数如下:

  • url:  请求的网址
  • data:要发送到服务器的数据
  • timeout:设置网站的访问超时时间

在urlopen()方法中,直接写入要访问的url地址字符串,该方法就会主动的访问目标网址,然后返回访问结果。

返回的访问结果是一个http.client.HTTPResponse对象,使用该对象的read()方法即可获取访问网页获取的数据,这个数据是二进制格式的,所以我们还需要使用decode()方法来将获取的二进制数据进行解码,转换成我们可以看的懂得字符串。

import urllib.request
req = urllib.request.urlopen(‘http://www.baidu.com‘)
print(req.read().decode())

for循环读取

for line in urlopen(‘https://。。.html‘):
    line = line.decode(‘utf-8‘)  # Decoding the binary data to text.
    if ‘EST‘ in line or ‘EDT‘ in line:  # look for Eastern Time
        print(line)

urlopen返回对象提供方法:

  • read():对HTTPResponse类型数据进行操作
  • readline():
  • readlines() :
  • fileno():
  • close() :
  • info():返回一个httplib.HTTPMessage 对象,表示远程服务器返回的头信息。
  • getcode():返回Http状态码,如果是http请求,200表示请求成功完成;404表示网址未找到。
  • geturl():返回请求的url。

3、GET请求

GET请求 和我们平常get访问方式一样,直接把参数写到网址上面就好了。

import urllib.request
import urllib.parse

dic = {‘name‘:‘melon‘,‘age‘:18}
data = urllib.parse.urlencode(dic)

req = urllib.request.urlopen(‘http://127.0.0.1:8000/index?%s‘%data) #通过urlopen方法访问拼接好的url

content = req.read()

4、POST请求

urlopen()方法中,urlopen()默认的访问方式是GET,当在urlopen()方法中传入data参数时,则会发起POST请求。注意:传递的data数据需要为bytes格式,如data=b‘word=hello‘。

import urllib.request
import urllib.parse
import json

dic = {‘name‘:‘melon‘,‘age‘:18}
data = urllib.parse.urlencode(dic)

req = urllib.request.Request(‘http://127.0.0.1:8000/index‘, data.encode()) #encode:将url编码类型的请求数据转变为bytes类型

opener = urllib.request.urlopen(req)
content = json.loads(opener.read().decode()) #read()方法是读取返回bytes数据内容,decode转换后为str

当你 urllib.urlopen一个 https 的时候会验证一次 SSL 证书,当目标使用的是自签名的证书时就会出现一个URLError,如果是这样可以在开头加上

import ssl
ssl._create_default_https_context = ssl._create_unverified_context

5、Request——请求头

当我们需要模拟一些其他的参数的时候,简单的urlopen() 方法已经无法满足我们的需求了,这个时候我们就需要使用urllib.request中的Request对象来帮助我们实现一些其它参数的模拟,比如请求头。

Request对象如下所示:

# Request对象实例化
req  = urllib.request.Request(url, data=None, headers={},origin_req_host=None,unverifiable=False, method=None)

举例如下:

from urllib import request
  
url = ‘http://httpbin.org/get‘
headers = {‘user-agent‘: ‘Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36‘}
  
# 需要使用url和headers生成一个Request对象,然后将其传入urlopen方法中
req = request.Request(url, headers=headers)
resp = request.urlopen(req)
print(resp.read().decode())

6、ProxyHandler—代理IP

使用爬虫来爬取数据的时候,如果过于频繁的访问,而且网站还设有限制的话,很有可能会禁封我们的ip地址,这个时候就需要设置代理,来隐藏我们的真实IP。

urllib提供了urllib.request.ProxyHandler()方法可动态的设置代理IP池。将代理IP以字典形式传入该方法,然后通过urllib.reques.build_opener()创建opener对象,用该对象的open()方法向服务器发送请求。

在上述的方法中我们还用到了一个新的东西,即request.build_opener()方法,其实urlopen()就是通过构造好了的opener对象发送请求,在这里我们使用request.build_opener()方法重构了一个opener()对象,我们可以通过这个对象实现urlopen()实现的任何东西。

from urllib import request
  
url = ‘http://httpbin.org/ip‘
proxy = {‘http‘: ‘218.18.232.26:80‘, ‘https‘: ‘218.18.232.26:80‘}
proxies = request.ProxyHandler(proxy)  # 创建代理处理器
opener = request.build_opener(proxies)  # 创建opener对象
  
resp = opener.open(url)
print(resp.read().decode())

7、HTTPCookieProcessor—cookies

有时候当我们访问一些网站的时候需要进行翻页或者跳转等其它操作,为了防止无法访问我们想要的数据,需要让网站识别我们是同一个用户。这个时候我们就需要带上cookie进行访问。

在设置cookie的时候由于urllib并没有很好的处理cookie的对象,所以在这里我们需要用到一个别的库,即http库,并使用里面的cookiejar来进行cookie的管理:

from http import cookiejar
from urllib import request
  
url = ‘https://www.baidu.com‘
# 创建一个cookiejar对象
cookie = cookiejar.CookieJar()
# 使用HTTPCookieProcessor创建cookie处理器
cookies = request.HTTPCookieProcessor(cookie)
# 并以它为参数创建Opener对象
opener = request.build_opener(cookies)
# 使用这个opener来发起请求
resp = opener.open(url)
  
# 查看之前的cookie对象,则可以看到访问百度获得的cookie
for i in cookie:
    print(i)

当然,如果把上面这个生成的opener对象使用install_opener方法来设置为全局的,opener对象之后的每次访问都会带上这个cookie。

设置全局后既可以用urlopen()方法, 也可以用opener.open() ,不安装的话只能用opener.open()方法

# 将这个opener设置为全局的opener,之后所有的,不管是opener.open()还是urlopen() 发送请求,都将使用自定义
request.install_opener(opener)
resp = request.urlopen(url)

8、复杂的请求—证书验证

通过添加忽略ssl证书验证关闭证书验证,由于urllib并没有很好的处理ssl的对象,所以在这里我们需要用到一个别的库,即ssl库,如下:

import ssl
from urllib import request
 
context = ssl._create_unverified_context()
res = urllib.request.urlopen(request, context=context)

三、urllib.error 模块

在urllib中主要设置了两个异常,一个是URLError,一个是HTTPError,HTTPError是URLError的子类。

HTTPError还包含了三个属性:

  1. code:请求的状态码
  2. reason:错误的原因
  3. headers:响应的报头

举例:

from urllib.error import HTTPError
  
try:
     request.urlopen(‘https://www.jianshu.com‘)
except HTTPError as e:
     print(e.code)

四、urllib3

通过urllib3访问一个网页,那么必须首先构造一个PoolManager对象,然后通过PoolMagent中的request方法或者 urlopen()方法来访问一个网页,两者几乎没有任何区别。

class urllib3.poolmanager.PoolManager(num_pools = 10,headers = None,** connection_pool_kw )
生成一个PoolManager所需要的参数:
  1. num_pools 代表了缓存的池的个数,如果访问的个数大于num_pools,将按顺序丢弃最初始的缓存,将缓存的个数维持在池的大小。
  2. headers 代表了请求头的信息,如果在初始化PoolManager的时候指定了headers,那么之后每次使用PoolManager来进行访问的时候,都将使用该headers来进行访问。
  3. ** connection_pool_kw 是基于connection_pool 来生成的其它设置

当访问网页完成之后,将会返回一个HTTPResponse对象,可以通过如下的方法来读取获取GET请求的响应内容:

import urllib3

data = json.dumps({‘abc‘: ‘123‘})
http = urllib3.PoolManager(num_pools=5, headers={‘User-Agent‘: ‘ABCDE‘})
  
resp1 = http.request(‘GET‘, ‘http://www.baidu.com‘, body=data)
 # resp2 = http.urlopen(‘GET‘, ‘http://www.baidu.com‘, body=data)
  
print(resp2.data.decode())
除了普通的 GET 请求之外,你还可以使用request()或者urlopen()进行 POST 请求:
import urllib3
import json
  
data = json.dumps({‘abc‘: ‘123‘})
http = urllib3.PoolManager(num_pools=5, headers={‘User-Agent‘: ‘ABCDE‘})
  
resp1 = http.request(‘POST‘, ‘http://www.httpbin.org/post‘, body=data,timeout=5,retries=5)
#resp2 = http.urlopen(‘POST‘, ‘http://www.httpbin.org/post‘, body=data,timeout=5,retries=5)
  
print(resp1.data.decode())
注意事项:

urllib3 并没有办法单独设置cookie,所以如果你想使用cookie的话,可以将cookie放入到headers中

request()和urlopen()方法的区别:

urlopen()比request()有三个参数是不一样的,你会发现request()具有fields,headers两个参数。而且相比之下 reuqest() 方法还是比较符合人们的使用方式的,所以更多的也就使用 request() 方法了。

  1. request(self, method, url, fields=None, headers=None, **urlopen_kw)
  2. urlopen(self, method, url, redirect=True, **kw):

推荐使用request()来进行访问的,因为使用request()来进行访问有两点好处,

  1. 可以直接进行post请求,不需要将 data参数转换成JSON格式
  2. 直接进行GET请求,不需要自己拼接url参数
import urllib3
import json
  
data = {‘abc‘: ‘123‘}
http = urllib3.PoolManager(num_pools=5, headers={‘User-Agent‘: ‘ABCDE‘})
  
resp1 = http.request(‘POST‘, ‘http://www.httpbin.org/post‘, fields=data)
# resp1 = http.request(‘GET‘, ‘http://www.httpbin.org/post‘, fields=data)  
  
print(resp1.data.decode())

虽然urlopen()没有fields,但这并不代表它不能进行POST访问,相反,两个方法都有一个body属性,这个属性就是我们平时POST请求时所熟知的data参数,只不过你需要将data字典先转换成JSON格式再进行传输。

不过特别要声明的一点是 fields 和 body 中只能存在一个。

ProxyManager

如果你需要使用代理来访问某个网站的话, 那么你可以使用 ProxyManager 对象来进行设置

def __init__(self, proxy_url, num_pools=10, headers=None,proxy_headers=None, **connection_pool_kw):
ProxyManager和PoolManager的方法基本完全相同,这里举个简单的小例子,就不再赘述了:
import urllib3
import json
 
data = {‘abc‘: ‘123‘}
proxy = urllib3.ProxyManager(‘http://50.233.137.33:80‘, headers={‘connection‘: ‘keep-alive‘})
resp1 = proxy.request(‘POST‘, ‘http://www.httpbin.org/post‘, fields=data)
print(resp1.data.decode())