Python 爬虫十六式 - 第二式:urllib 与 urllib3
Python请求标准库 urllib 与 urllib3
学习一时爽,一直学习一直爽!
大家好,我是 Connor,一个从无到有的技术小白。上一次我们说到了什么是HTTP协议,那么这一次我们就要动手,来真正的了解如何使用Python访问一个网站了。今天我们要说的是Python自带的标准库,Urllib与Urllib3。
1.urllib库
1.1urllib的简介
urllib`是Python中请求url连接的官方标准库,在Python2中主要为urllib和urllib2,在Python3中整合成了urllib。urllib中一共有四个模块,分别是 request,error,parse,robotparser,下面我们就来看一下urllib怎么使用吧。
1.2 urllib.request库
urllib.request模块定义了在复杂世界中帮助打开URLs (主要是HTTP )的函数和类——基本和摘要认证、重定向、cookies等等,这个模块主要负责构造和发起网络请求,并在其中加入Headers、Proxy等。
1.2.1 发起 GET 请求
如果你想打开一个网页,那么使urlopen()
方法来发起请求无疑是正确的选择,你可以这样做:
from urllib import request
resp = reqeust.urlopen('https://www.baidu.com')
print(resp.read().decode())
在urlopen()
方法中,直接写入要访问的url地址字符串,该方法就会主动的访问目标网址,然后返回访问结果,返回的访问结果是一个 http.client.HTTPResponse
对象,使用该对象的read()
方法即可获取访问网页获取的数据,这个数据是二进制格式的,所以我们还需要使用decode()
方法来将获取的二进制数据进行解码,转换成我们可以看的懂得字符串。
1.2.2 发起 POST 请求
在urlopen()
方法中,urlopen()
默认的访问方式是GET,当在urlopen()
方法中传入data参数时,则会发起POST请求。注意:传递的data数据需要为bytes格式,你可以这样做:
from urllib import reuqest
resp = request.urlopen('http://httpbin.org/post', data=b'word=hello')
print(resp.read().decode())
1.2.3 Request对象
在urlopen()
中不止可以传入字符串格式的url,也可以传入Request
对象来实现功能的拓展,Request
对象如下所示:
class urllib.request.Request(url, data=None, headers={},
origin_req_host=None,
unverifiable=False, method=None)
1.2.4 其它参数
当我们需要模拟一些其他的参数的时候,简单的urlopen()
方法已经无法满足我们的需求了,这个时候我们就需要使用urllib.request
中的Request
对象来帮助我们实现一些其它参数的模拟,这些需要模拟的其它参数有如下几种:
- Headers:
通过urllib发起请求的时候会有一个默认的Headers,这个Headers是"User-Agent": “Python-urllib/3.x”,如果网站设有UA验证,那么我们的程序无法访问成功,这个时候我们就需要伪装UA来进行访问,直接使用Request对象来添加Headers即可:
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())
- Cookie:
有时候当我们访问一些网站的时候需要进行翻页或者跳转等其它操作,为了防止无法访问我们想要的数据,需要让网站识别我们是同一个用户。这个时候我们就需要带上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)
在上述的方法中我们还用到了一个新的东西,即request.build_opener()
方法,其实urlopen()
就是一个构造好了的opener对象,在这里我们使用request.build_opener()
方法重构了一个opener()
对象,我们可以通过这个对象实现urlopen()
实现的任何东西。
当然,如果把这个生成的opener使用install_opener方法来设置为全局的,那么这个opener就是全局的,之后的每次访问都会带上这个cookie。
# 将这个opener设置为全局的opener
request.install_opener(opener)
resp = request.urlopen(url)
- Proxy:
使用爬虫来爬取数据的时候,如果过于频繁的访问,而且网站还设有限制的话,很有可能会禁封我们的ip地址,这个时候就需要设置代理,来隐藏我们的真实IP,代理需要创建一个代理处理器,使用request.ProxyHandler
来生成一个代理处理器,你应该这样做:
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())
1.2.5 请求中需要注意的事情
decode()
方法中,如果你不写解码格式,那么它会自动的将二进制格式转码成当前编辑器环境的格式,通常为UTF-8
,当然,有些网站的编码格式并不会使用utf-8
格式来编写网站,这个时候就需要你指定解码格式,如果你不清楚网站的解码格式的话,可以尝试查看网页源代码,ctrl + F
来查找charset
属性,这时候直接指定解码格式即可,当然如果网站中并没有写明charset
属性的话,那就只有多试几次了…urlopen()
,opener的open()
中的data数据是二进制的,一定要先将它们用encode()
方法转换成二进制数据中再进行发送。
1.3 urllib.response
在使用urlopen()
方法或者opener的open()
方法发起请求后,获得的结果是一个response
对象。
这个对象有一些方法和属性,可以让我们对请求返回的结果进行一些处理。
read()
获取响应返回的数据,只能使用一次。
getcode()
获取服务器返回的状态码。
getheaders()
获取返回响应的响应报头。
geturl()
获取访问的url。
1.4 urllib.parse
urllib.parse`是urllib中用来解析各种数据格式的模块。这之中有我们常用的两个方法,所以我们只着重说两个方法:
1.4.1 urllib.parse.quote
再url中,只能使用ASCII中包含的字符,也就是说,ASCII不包含特殊字符,包括中文,韩语,日语等各种字符,这些字符都无法在url中进行使用,而我们有的时候又需要将一些中文字符加入到url中,例如百度的搜索:
?
之后的wd参数,就是我们搜索的关键词。我们实现的方法就是将特殊字符进行url编码,转换成可以使用url传输的格式,urlib中可以使用quote()
方法来实现这个功能。
In [1]: from urllib import parse
In [2]: parse.quote('底特律')
Out[2]: '%E5%BA%95%E7%89%B9%E5%BE%8B'
这样我们就把中文字符串转换成了url能够识别的形式了,但是有的时候你会发现你明明进行了转码,但是访问的时候还是会无法访问,原因在于你的编码格式和网站指定的编码格式并不相符。
urllib.parse.quote
(string, safe=’/’, encoding=None, errors=None)
在上面的方法中不难看到,quote拥有多个参数,你可以通过指定编码格式的方法来确保你转码后获得的数据是正确的。
In [1]: from urllib import parse
In [2]: parse.quote("底特律", encoding='utf-8')
Out[2]: '%E5%BA%95%E7%89%B9%E5%BE%8B'
In [3]: parse.quote("底特律", encoding='gbk')
Out[3]: '%B5%D7%CC%D8%C2%C9'
效果还是很明显的,明显的指定不同格式后的字符串编码是不同的。当然,如过你需要解码的时候,你也可以使用unquote()
方法来解码:
In [1]: from urllib import parse
In [2]: parse.unquote('%E5%BA%95%E7%89%B9%E5%BE%8B',encoding='utf-8')
Out[2]: '底特律'
1.4.2 urllib.parse.urlencode
在访问url时,我们常常需要传递很多的url参数,而如果用字符串的方法去拼接url的话,会比较麻烦,所以urllib
中提供了urlencode
这个方法来拼接url参数,该方法同样支持指定编码格式。
In [1]: from urllib import parse
In [2]: paramers = {'address': '底特律', 'phone': '123456789', 'name': 'Connor'}
In [3]: parse.urlencode(paramers)
Out[3]: 'address=%E5%BA%95%E7%89%B9%E5%BE%8B&phone=123456789&name=Connor'
In [4]: parse.urlencode(paramers,encoding='utf-8')
Out[4]: 'address=%E5%BA%95%E7%89%B9%E5%BE%8B&phone=123456789&name=Connor'
In [5]: parse.urlencode(paramers,encoding='gbk')
Out[5]: 'address=%B5%D7%CC%D8%C2%C9&phone=123456789&name=Connor'
1.5 urllib.error
在urllib
中主要设置了两个异常,一个是URLError
,一个是HTTPError
,HTTPError
是URLError
的子类。
HTTPError
还包含了三个属性:
- code:请求的状态码
- reason:错误的原因
- headers:响应的报头
例子:
In [1]: from urllib.error import HTTPError
In [2]: try:
...: request.urlopen('https://www.jianshu.com')
...: except HTTPError as e:
...: print(e.code)
403
2.urllib3
其实我原本放弃写urllib3的打算了,因为相比requests来说,urllib和urllib3都显得太繁琐了,乱七八糟的功能导致几乎没有人写urlib3的说明,现有的urllib3几乎都是出自一人之手,大家相互抄都抄烂了… 我就简单的写一点吧,不见得比抄烂了的好,大家见谅吧。
2.1 urllib3 简介
urllib3是一个功能强大,对SAP 健全的 HTTP客户端。许多Python生态系统已经使用了urllib3,你也应该这样做。 ——来自官方的自述
urllib一共有两个子包和八个模块,包几乎用不到,八个模块中并不是所有的都是我们常用的东西,大概有六个左右是我们经常用的,下面我们来看一下这几个模块吧
2.2 urllib3 PoolManager对象
2.2.1 urllib3.PoolMagent
如果你想通过urllib3访问一个网页,那么你必须先构造一个PoolManager对象,然后通过PoolMagent中的request方法来访问一个网页。
class urllib3.poolmanager.PoolManager(num_pools = 10,headers = None,** connection_pool_kw )
这是生成一个PoolMaager所需要的参数:
num_pools
代表了缓存的池的个数,如果访问的个数大于num_pools
,将按顺序丢弃最初始的缓存,将缓存的个数维持在池的大小。headers
代表了请求头的信息,如果在初始化PoolManager
的时候制定了headers
,那么之后每次使用PoolManager
来进行访问的时候,都将使用该headers来进行访问。** connection_pool_kw
是基于connection_pool 来生成的其它设置,这里我们不细述
2.2.2 request() 和 urlopen() 方法
当生成了一个PoolManager之后,我们就可以开始访问我们需要访问的网页了。
你可以通过 urlopen()
方法或者request()
方法来访问一个网页,当你访问完成之后,将会返回一个HTTPREsopnse对象给你,你可以通过如下的方法来读取获取的响应内容:
import urllib3
http = urllib3.PoolManager(num_pools=5, headers={'User-Agent': 'ABCDE'})
resp1 = http.request('GET', 'http://www.baidu.com', body=data)
print(resp1.data.decode())
当然,你也可以使用urlopen()
方法来打开一个网页,他们两者几乎没有任何区别:
import urllib3
http = urllib3.PoolManager(num_pools=5, headers={'User-Agent': 'ABCDE'})
resp2 = http.urlopen('GET', 'http://www.baidu.com', body=data)
print(resp2.data.decode())
除了普通的 GET 请求之外,你还可以使用request()
进行 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)
print(resp1.data.decode())
当然,request()
能做的东西,urlopen()
都能做:
import urllib3
import json
data = json.dumps({'abc': '123'})
http = urllib3.PoolManager(num_pools=5, headers={'User-Agent': 'ABCDE'})
resp2 = http.urlopen('POST', 'http://www.httpbin.org/post', body=data)
print(resp1.data.decode())
你还可以为他们设置超时时间,只需要添上timeout
参数即可:
import urllib3
http = urllib3.PoolManager(num_pools=5, headers={'User-Agent': 'ABCDE'})
resp1 = http.request('GET', 'http://www.baidu.com', body=data, timeout=5)
print(resp1.data.decode())
import urllib3
http = urllib3.PoolManager(num_pools=5, headers={'User-Agent': 'ABCDE'})
resp1 = http.urlopen('GET', 'http://www.baidu.com', body=data, timeout=5)
print(resp1.data.decode())
你也可以使用retries
设置重试次数:
import urllib3
http = urllib3.PoolManager(num_pools=5, headers={'User-Agent': 'ABCDE'})
resp1 = http.request('GET', 'http://www.baidu.com', body=data, timeout=5, retries=5)
print(resp1.data.decode())
import urllib3
http = urllib3.PoolManager(num_pools=5, headers={'User-Agent': 'ABCDE'})
resp1 = http.urlopen('GET', 'http://www.baidu.com', body=data, timeout=5, retries=5)
print(resp1.data.decode())
注意事项
- urllib3 并没有办法单独设置cookie,所以如果你想使用cookie的话,可以将cookie放入到headers中
2.2.3 request() 和 urlopen() 的区别
我个人还是比较建议使用request()
来进行访问的,因为相比之下,使用request()
来进行访问,原因很简单,我们可以看一下ruquest()
和urlopen()
之间的差距:
def request(self, method, url, fields=None, headers=None, **urlopen_kw):
def urlopen(self, method, url, redirect=True, **kw):
如果你用的是 request()
方法的话,那你还可以通过这种方式来直接进行post请求,不需要将 data
参数转换成JSON格式:
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', field=data)
print(resp1.data.decode())
如果你用的是 request()
方法的话,那你还可以通过这种方式来直接进行GET请求,不需要自己拼接url参数:
import urllib3
import json
data = {'abc': '123'}
http = urllib3.PoolManager(num_pools=5, headers={'User-Agent': 'ABCDE'})
resp1 = http.request('GET', 'http://www.httpbin.org/post', field=data)
print(resp1.data.decode())
差距还是很明显的,urlopen()
比request()
有三个参数是不一样的,你会发现request()
多了fields,headers两个参数。而且相比之下 reuqest()
方法还是比较符合人们的使用方式的,所以更多的也就使用 request()
方法了。
虽然urlopen()
没有fielder,但这并不代表它不能进行POST访问,相反,两个方法都有一个body
属性,这个属性就是我们平时POST请求时所熟知的data参数,只不过你需要将data字典先转换成JSON
格式再进行传输。fielder
属性其实也是用来传输data
的,但是它的功能是可以直接将字典对象传入,无需再进行json格式转换了。如果你用的是GET
方式的话,那fielder可以帮你进行url拼接,如果是POST
方式的话,filder可以帮你主动转换成JSON格式。不过特别要声明的一点是 fielder 和 body 中只能存在一个,这个功能说实话,个人觉得写这两个特别类似的方法… Emmn… 总是有点鸡肋的感觉。
2.2 urllib3 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', field=data)
print(resp1.data.decode())
好啦,这就是全部的urllib和urllib3的文章啦,恭喜你终于读完了这篇文章,不知道今天你又学会了多少呢?
我是Connor,一个从无到有的技术小白,这一次我们就讲到这里啦,我们下期再见!
学习一时爽,一直学习一直爽
系列文章连接:
Python 爬虫十六式 - 第一式:HTTP协议 >>>
Python 爬虫十六式 - 第三式:Requests的用法 >>>
Python 爬虫十六式 - 第四式: 使用Xpath提取网页内容>>>Python 爬虫十六式 - 第五式:BeautifulSoup-美味的汤 >>>
Python 爬虫十六式 - 第六式:JQuery的假兄弟-pyquery >>>
Python 爬虫十六式 - 第七式:正则的艺术 >>>
Python 爬虫十六式 - 第二式:urllib 与 urllib3的更多相关文章
- Python 爬虫十六式 - 第六式:JQuery的假兄弟-pyquery
PyQuery:一个类似jquery的python库 学习一时爽,一直学习一直爽 Hello,大家好,我是 Connor,一个从无到有的技术小白.上一次我们说到了 BeautifulSoup 美味 ...
- Python 爬虫十六式 - 第七式:正则的艺术
RE:用匹配来演绎编程的艺术 学习一时爽,一直学习一直爽 Hello,大家好,我是 Connor,一个从无到有的技术小白.上一次我们说到了 pyquery 今天我们将迎来我们数据匹配部分的最后一位 ...
- Python 爬虫十六式 - 第五式:BeautifulSoup-美味的汤
BeautifulSoup 美味的汤 学习一时爽,一直学习一直爽! Hello,大家好,我是Connor,一个从无到有的技术小白.上一次我们说到了 Xpath 的使用方法.Xpath 我觉得还是 ...
- Python爬虫十六式 - 第四式: 使用Xpath提取网页内容
Xpath:简单易用的网页内容提取工具 学习一时爽,一直学习一直爽 ! Hello,大家好,我是Connor,一个从无到有的技术小白.上一次我们说到了 requests 的使用方法.到上节课为止, ...
- Python爬虫十六式 - 第三式:Requests的用法
Requests: 让 HTTP 服务人类 学习一时爽,一直学习一直爽 Hello,大家好,我是Connor,一个从无到有的技术小白.今天我们继续来说我们的 Python 爬虫,上一次我们说到了 ...
- Python 爬虫十六式 - 第一式:HTTP协议
HTTP:伟大而又无闻的协议 学习一时爽,一直学习一直爽! Hello,大家好啊,我是Connor,一个从无到有的技术小白.有的人一说什么是HTTP协议就犯愁,写东西的时候也没想过什么是HTTP协 ...
- Python爬虫(十六)_JSON模块与JsonPath
本篇将介绍使用,更多内容请参考:Python学习指南 数据提取之JSON与JsonPATH JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,它是的人们很容易 ...
- Python爬虫实战六之抓取爱问知识人问题并保存至数据库
大家好,本次为大家带来的是抓取爱问知识人的问题并将问题和答案保存到数据库的方法,涉及的内容包括: Urllib的用法及异常处理 Beautiful Soup的简单应用 MySQLdb的基础用法 正则表 ...
- Python爬虫入门六之Cookie的使用
大家好哈,上一节我们研究了一下爬虫的异常处理问题,那么接下来我们一起来看一下Cookie的使用. 为什么要使用Cookie呢? Cookie,指某些网站为了辨别用户身份.进行session跟踪而储存在 ...
随机推荐
- 从MAP角度理解神经网络训练过程中的正则化
在前面的文章中,已经介绍了从有约束条件下的凸优化角度思考神经网络训练过程中的L2正则化,本次我们从最大后验概率点估计(MAP,maximum a posteriori point estimate)的 ...
- http状态码 超详细
100 Continue 这个临时响应表明,迄今为止的所有内容都是可行的,客户端应该继续请求,如果已经完成,则忽略它. 101 Switching Protocol 该代码是响应客户端的 Upgrad ...
- AGC035 B - Even Degrees【思维·树形结构的妙用】
题目传送门 一句话题意: 首先,每一条边会产生1个入度,1个出度,因此,如果边的数量是奇数的话,图的所有节点的总出度就是奇数,不可能每个节点的出度都是偶数,因此无解. 有解时,我们先找出原图中的一棵生 ...
- 深入理解java:3. NIO 编程
I/O简介 I/O即输入输出,是计算机与外界世界的一个借口. IO操作的实际主题是操作系统. 在Java编程中,一般使用流的方式来处理IO,所有的IO都被视作是单个字节的移动,通过stream对象一次 ...
- mavn Nexus Repository Manager漏洞
https://www.secpulse.com/archives/112290.html
- DataLinkLayer(数据链路层)
DataLinkLayer(数据链路层) The Reference Model(参考模型) layer name 5 Application layer 4 TransPort layer 3 Ne ...
- numpy将数组保存为文件
保存单个数组 np.save和np.load是读写磁盘数组数据的两个主要函数.默认情况下,数组是以未压缩的原始二进制格式保存在扩展名为.npy的文件中的: 如果文件路径末尾没有扩展名.npy,则该扩展 ...
- Linux免密登陆设置了免密登陆为啥还需要输入密码
一.设置了免密码登陆但是还是需要输入密码: 权限保证:1.authorized-keys 的权限为 600 2.home.账户所在的目录如hadoop..ssh这三个文件的权限都必须设置为700,缺少 ...
- 解决iframe缓存机制导致页面不清除缓存不刷新页面的bug
在使用iframe时,已有页面嵌套了一个iframe页面,当这个页面提交后再次跳转到本页面时,原本iframe内的页面应该刷新数据的,结果未刷新,需要清除缓存后才刷新. 解决方案: var fresh ...
- 腾讯万亿级分布式消息中间件TubeMQ正式开源
TubeMQ是腾讯在2013年自研的分布式消息中间件系统,专注服务大数据场景下海量数据的高性能存储和传输,经过近7年上万亿的海量数据沉淀,目前日均接入量超过25万亿条.较之于众多明星的开源MQ组件,T ...