Python 爬虫十六式 - 第二式: urllib 与 urllib3

Python请求标准库 urllib 与 urllib3

学习一时爽,一直学习一直爽!python

  你们好,我是 Connor,一个从无到有的技术小白。上一次咱们说到了什么是HTTP协议,那么这一次咱们就要动手,来真正的了解如何使用Python访问一个网站了。今天咱们要说的是Python自带的标准库,Urllib与Urllib3。json

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()方法来发起请求无疑是正确的选择,你能够这样作:cookie

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对象来帮助咱们实现一些其它参数的模拟,这些须要模拟的其它参数有以下几种:post

  • 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 请求中须要注意的事情
  1. decode()方法中,若是你不写解码格式,那么它会自动的将二进制格式转码成当前编辑器环境的格式,一般为UTF-8,固然,有些网站的编码格式并不会使用utf-8格式来编写网站,这个时候就须要你指定解码格式,若是你不清楚网站的解码格式的话,能够尝试查看网页源代码,ctrl + F来查找charset属性,这时候直接指定解码格式便可,固然若是网站中并无写明charset属性的话,那就只有多试几回了...

  2. 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中,例如百度的搜索:

www.baidu.com/s?wd=底特律

  ?以后的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,一个是HTTPErrorHTTPErrorURLError的子类。

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所须要的参数:

  1. num_pools 表明了缓存的池的个数,若是访问的个数大于num_pools,将按顺序丢弃最初始的缓存,将缓存的个数维持在池的大小。
  2. headers 表明了请求头的信息,若是在初始化PoolManager的时候制定了headers,那么以后每次使用PoolManager来进行访问的时候,都将使用该headers来进行访问。
  3. ** 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 爬虫十六式 - 第八式:实例解析-全书网 >>>

相关文章
相关标签/搜索