urllib初探
Posted rajxie
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了urllib初探相关的知识,希望对你有一定的参考价值。
urllib库
简介
urllib是python内置的HTTP请求库。它包含以下4个模块:
- request : 最基本的HTTP请求模块。用于模拟发送请求。
- error : 异常处理模块。用于捕获异常,排除错误对程序正常进行的干扰。
- parse : 工具模块。用于处理URL,如拆分、合并、解析。
- robotparser : 主要用于识别网站的robot.txt文件(爬虫协议,并判断哪些资源可供爬取)。
通过urllib库来获取网页内容是一个福音。我们只需关心请求的链接是什么,需要传的参数是什么,如何设置可选的请求头即可,而无需深入到底层了解它到底是怎么传输和通信的。
使用
构造请求
使用urllib的request模块构造HTTP请求,实现请求的发送并获取响应。
1. urlopen()
urlopen的API:urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)
- 第一个参数url: 用于请求URL。
- 第二个参数data: 用于在请求中附加数据。(附加数据的请求一般被认为是POST方式的请求,因为只有POST才需要提交一些表单(data form)信息。否则就是GET方式)
- 第三个参数timeout: 用于设置超出时间(单位秒),支持HTTP、HTTPS、 FTP请求。指定该参数,若请求在设置的时间内得不到响应,则抛出异常。不指定使用全局默认时间。
- 其他参数:
url参数:
- ① 将urllib.request模块导入当前的工作环境中
- ② 使用urlopen()方法,构造一个HTTP请求赋值给response变量(HTTP请求的作用是:实现请求的发送并获取响应)
- ③ 使用type()方法,发现response变量为一个HTTPResponse类型的对象
- ④ 通过对象名.read()方法就可以得到返回的网页内容了。
通过②③,我们知道了在urlopen()函数里传入一个URL,将返回一个HTTPResponse类型的对象。是对象就包含一些属性和方法,对吧?哈哈:
read()、readinto()、getheader(name)、getheaders()、fileno()等方法;msg、version、status、reason、debuglevel、closed等属性。
- 调用status属性,得到该对象的状态码;
- 调用getheaders()方法,得到返回内容的全部头部信息;
- 调用getheader(name)方法,得到特定部分的头部信息了。
注意:getheaders()是复数形式,返回头部的全部信息;getheader(name)是单数的,表示按需获取。
对比:
库名 | urllib库 | requests库 |
---|---|---|
构造请求 | request.urlopen(url) | get(url) |
响应状态 | status | status_code |
响应内容 | read() | text |
加入Headers等信息 | 先行构建一个Request类型的对象,并使之作为request.urlopen()方法的参数 | 直接在get()方法中使用headers参数 |
如何理解使用urllib获取的响应体是使用read()方法来解析?姑且将其获得的响应体看作一个file-like对象,而对文件使用read()方法自然说得过去了。
data参数:如果传递了这个参数,则请求方式不再是GET方式,而是POST方式。该参数为bytes类型,需使用bytes()方法转化。
传递的参数出现在了form字段中,说明是表单提交。这是一个POST请求确证凿凿。
timeout参数:用于设置超时时间。若请求超出了设置的这个时间尚未获取响应,将抛出异常。不指定则会使用全局默认时间。
出现异常:
捕获异常:
其他参数:
cafile和capath分别指定CA证书和它的路径,在请求HTTPS链接是会有用。
cadefault参数现已弃用,其默认值为False。
context参数则必须为ssl.SSLContext类型,用来指定SSL设置。
2. Request
urlopen()方法可以实现最基本的请求的发起,但这个请求并不完整。如果请求中需要加入Headers等信息,就需要通过Request类来构建了。
通过构造Request数据结构,一方面可以将请求独立为一个对象,另一方面我们也可以更加灵活地配置参数。这颇有点囊括宇宙的意味。
Request的API:urllib.request.Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None)
- 第一个参数url: 用于请求URL
- 第二个参数data: bytes类型内容。若为字典,可先用urllib.parse.urlencode()方法编码为字符串。
- 第三个参数headers: 请求头。通过headers(字典类型)直接构造,或通过请求实例的add_header()方法构造。
- 第四个参数origin_req_host: 请求方的host名称或IP地址。
- 第五个参数unverifiable: 布尔类型,表示请求是否是无法验证的。默认False(即有足够的权限来接收这个请求的权限)。
- 第六个参数method: 字符串类型,表示请求使用的方法。如GET、POST等。
除了第一个参数为必选的之外,其余均为可选参数。
上面是很教条化的东西。下面,让我们看两个例子吧。
第一个例子:
① 先导入urllib.request模块;
② 再通过向urllib.request模块中的Request类传入一个URL构建一个请求实例;
③ 之后,我们再将请求对象作为urlopen()方法的参数来获取服务器的响应;
④ 最后,打印出请求的内容。
整个流程分为:URL构建请求对象→ 传入请求对象获取响应 → 输出响应内容
第二个例子:
① 先导入urllib.request模块和urllib.parse模块;
② 确定url——使用赋值的方式,将url具体化。直接传入urlopen()也无碍,这里是为了防止api的书写过长;
③ 构建一个请求头(通过User-Agent可以模拟任何的浏览器,防止爬虫身份的暴露);
④ 构建一个字典存储表单的信息。由于该参数要求为字节流格式,我们还需要分两步走:
先将之序列化——使用urllib.parse模块的urlencode()方法,
再将之转换为字节流编码格式的内容——使用bytes()函数
⑤ 准备齐全就可以开始构建一个Request类的实例了。下面的例子将之赋值给request
⑥ 最后,将请求对象传入urllib.request模块的urlopen()方法,成功获取了服务器的响应并打印出响应内容。
3. 高级用法
上面我们所构建的请求,对于一些更为高级的操作(如登录认证,代理设置,Cookies处理等等)就显得无能为力了。为此,引入了Handler和Opener,以便深入一层进行配置(通过使用底层的实例来完成了)。
(1) Handler对应BaseHandler类(所有其他Handler的父类):
(处理器之意,正所谓“术业”chu li qi 有专攻:有处理登录验证的,有处理Cookies的,有处理代理设置的)。
- HTTPDefaultErrorHandler: 用于处理HTTP响应错误,错误都会抛出HTTPError类型的异常。
- HTTPRedirectHandler: 用于处理重定向。
- HTTPCookieProcessor: 用于处理Cookies。
- ProxyHandler: 用于设置代理,默认代理为空。
- HTTPPasswordMgr: 用于管理密码,即维护用户名和密码组成的表。
- HTTPBasicAuthHandler: 用于管理验证。
(2) Operner对应OpenerDirector类:
Opener可以使用open()方法,返回类型和urlopen()一致。
PS:之前使用的Request和urlopen()方法,是一个默认的opener(相当类库为我们封装好了的常用的请求方法)。
(3) Opener与Handler的关系:使用Handler来构建Opener(实例化一个Opener, 然后再通过它调用一些handler实例)。
- build_opener: 默认添加了数个handlers。同时作为一个提供快速添加或重写默认handlers的途径。
- install_opener: 使一个opener对象作为全局的默认opener,即urlopen()
下面,来观望几个例子:
验证
from urllib.request import HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler, build_opener from urllib.error import URLError username = ‘username‘ password = ‘password‘ url = ‘http://localhost:5000/‘ p = HTTPPasswordMgrWithDefaultRealm() p.add_password(None, url, username, password) auth_handler = HTTPBasicAuthHandler(p) opener = build_opener(auth_handler) try: result = opener.open(url) html = result.read().decode(‘utf-8‘) print(html) except URLError as e: print(e.reason)
这个例子使用了一个add_password之后的HTTPPasswordMgrWithDefaultRealm对象,作为参数构造了一个HTTPBasicAuthHandler实例,即处理验证的Handler。接着将该Handler传给build_opener()方法构建一个Opener。一旦这个Opener发送请求,则代表验证成功。
代理
from urllib.request import ProxyHandler, build_opener from urllib.error import URLError proxy_handler = ProxyHandler({ ‘http‘: ‘http://127.0.0.1:5000‘, ‘https‘: ‘https://127.0.0.1:5000‘ }) opener = build_opener(proxy_handler) try: response = opener.open(‘https://www.baidu.com‘) print(response.read().decode(‘utf-8‘)) except URLError as e: print(e.reason)
Cookies
import http.cookiejar, urllib.request from urllib.error import URLError cookie = http.cookiejar.CookieJar() handler = urllib.request.HTTPCookieProcessor(cookie) opener = urllib.request.build_opener(handler) try: response = opener.open(‘http://www.baidu.com‘) except URLError as e: print(e.reason) for item in cookie: print(item.name + ‘=‘ + item.value)
处理异常
urllib.error模块定义了由request模块产生的异常。也就是说,若程序出现了问题,request模块会抛出error模块中定义的异常。
URLError类: 继承自OSError类,是error异常的基类。因此,由request模块产生的异常都可以通过这个类来处理。
(有一个属性)
- reason: 返回错误的原因。
HTTPError类: URLError类的子类,专门用于处理HTTP请求错误,比如认证失败等。
(有三个属性)
- code: 返回HTTP状态码。
- reason: 返回错误的原因。
- headers: 返回请求头。
注意:有时,reason属性返回的不一定是字符串,也可能是一个对象。
import urllib.request import urllib.error import socket try: response = urllib.request.urlopen(‘https://www.baidu.com‘, timeout=0.01) except urllib.error.URLError as e: print(type(e.reason)) if isinstance(e.reason, socket.timeout): print(‘TIME OUT‘)
总结
以上的介绍,主要涉及到urllib库中的urllib.request和urllib.error模块。
urllib.request模块主要用于构造请求。使用urlopen()可以构造基本的请求,使用Request类构建更为完整的请求,使用Handler和Opener类可以构造更为高级的请求。
- urlopen()主要用到的参数有url、data、timeout,这里需要注意的是data参数应当为bytes类型。
- Request()则将请求独立为一个对象,使能灵活地配置参数,主要用到的有url、data、headers、method。
- Handler和Opener类则是将请求深入地进行配置,主要是将某一特定的handler作为build_opener()的参数构建一个Opener,接着使用open()方法则可以打开链接了。
后两者与urlopen()的关系:
- 由于Request()实例化之后是供urlopen()作为参数使用,因而两者在参数上有所交叉是无可厚非的;
- 而urlopen()是urllib为我们提供的一个默认的Opener,因而对于Opener使用open()返回的类型与urlopen()的一致也不必感到诧异。
urllib.error模块主要用于处理urllib.request模块产生的异常。其中URLError类异常是HTTPError类的基类,在处理时应当先使用范围较小的异常类进行处理,再涵盖范围较大的。
本文参考:《Python3 网络爬虫开发实战》
以上是关于urllib初探的主要内容,如果未能解决你的问题,请参考以下文章
Python练习册 第 0013 题: 用 Python 写一个爬图片的程序,爬 这个链接里的日本妹子图片 :-),(http://tieba.baidu.com/p/2166231880)(代码片段