一. urllib库与urllib3库的简介
python3中有urllib和urllib3两个库,其中urllib几乎是python2.7中urllib和urllib2两个模块的集合,所以我们最常用的是模块urllib模块,而urllib3则作为一个拓展模块使用。
(一)什么是Urllib库
Urllib库是Python中的一个功能强大、用于操作URL,并在做爬虫的时候经常要用到的库。在Python2.x中,分为Urllib库和Urllib2库,Python3.x之后都合并到Urllib库中,使用方法稍有不同。下面介绍的是Python3中的urllib库。
urllib 中包含四个模块,分别是 request
、parse
、 error
、 robotparser
,他们各自的主要作用是:
- request:发送请求
- parse:解析链接
- error:处理异常
- robotparser:分析 Robots 协议
以下我们将会分别讲解 Urllib 中各模块的使用方法,但是由于篇幅问题,本文只会涉及模块中比较常用的内容。
在开始讲解前,先给大家提供一个用于测试的网站:发送请求,这个网站可以在页面上返回所发送 请求 的相关信息,十分适合练习使用。
(二)什么是urllib3库
Urllib3是一个功能强大,条理清晰,用于HTTP客户端的Python库,许多Python的原生系统已经开始使用urllib3。Urllib3提供了很多python标准库里所没有的重要特性:
- 线程安全
- 连接池
- 客户端SSL/TLS验证
- 文件分部编码上传
- 协助处理重复请求和HTTP重定位
- 支持压缩编码
- 支持HTTP和SOCKS代理
- 100%测试覆盖率
(三)urllib库与urllib3库的关系
我们要明白的是,urllib库和urllib3库没什么关系,这两个库是两个完全不同的包。urllib库并不是在python3.X版本中的urllib3库。
二. urllib库模块详析
本节我们分别详细的讲述一下urllib库的四个重要的模块。关于urllib库的详细内容可参考其官方文档:https://docs.python.org/3/library/urllib.html。下面我们着重说一下前三个urllib模块(最后一个模块应用的频率较少,此处我们只作了解)。
(一)发送请求
request 模块是最基本的 HTTP 请求模块,可以用来模拟发送请求,就像在浏览器里输入网址,然后回车一样,只需要给库方法传入 URL 及额外的参数,就可以模拟实现这个过程了。同时它还带有处理授权验证( authenticaton )、重定向( redirection 、浏览器 Cookies 及其他内容。使用 urllib 的 request 模块,我们可以很方便地实现请求的发送并得到响应。
1. urlopen方法
urlopen方法的作用是快速发送一个简单的网络请求,该方法的调用格式如下:
urllib.request.urlopen(url, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, cafile=None, capath=None, cadefault=False, context=None)
urlopen 方法无疑是 request 模块中最常用的方法之一,常见的参数说明如下:
url
:必填,字符串,指定目标网站的 URL。data
:指定表单数据,该参数默认为 None,此时 urllib 使用 GET 方法 发送请求;当给参数赋值后,urllib 使用 POST 方法 发送请求,并在该参数中携带表单信息(bytes 类型)。例如下面这个例子:
from urllib import request, parse
data = bytes(parse.urlencode({
'word': 'hello'}), encoding='utf-8')
response = request.urlopen('http://httpbin.org/post', data=data)
print(response.read().decode('utf-8'))
# 运行结果:
{
"args": {
},
"data": "",
"files": {
},
"form": {
"word": "hello"
},
"headers": {
"Accept-Encoding": "identity",
"Content-Length": "10",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
"User-Agent": "Python-urllib/3.6",
"X-Amzn-Trace-Id": "Root=1-5f6a064d-10cc915066a3cda010364b70"
},
"json": null,
"origin": "111.44.171.116",
"url": "http://httpbin.org/post"
}
代码解析:我们传递了一个参数word,其值是“hello”。他需要被转码成bytes
(字节流)类型。其中转字节流采用bytes()方法,该方法的第一个参数需要是str(字符串)类型,需要用parse模块中的urlencode()方法将参数字典转换为字符串;第二个参数指定编码格式,这里指定为“utf-8”。本次请求的站点是httpbin.org
,他可以提供HTTP请求测试。当请求被发送后,我们将返回结果以read()
方法打印出来。
timeout
:可选参数,用来指定等待时间,若超过指定时间还没获得响应,则抛出一个异常。如果不指定该参数,就会使用全局默认时间。它支持HTTP、HTTPS、FTP请求。
from urllib import request
response = request.urlopen('http://httpbin.org/get', timeout=0.1)
print(response.read())
# 运行结果:
Traceback (most recent call last):
......
urllib.error.URLError: <urlopen error timed out>
此处我们设置超时时间是0.1秒。程序0.1秒过后,服务器依然没有响应,于是抛出了URLError异常。该异常属于urllib.error模块,错误原因是超时。因此,我们可以通过设置这个超时时间来控制一个网页如果长时间未响应,就跳过它的抓取。一般利用try … except语句来实现。
该方法始终返回一个 HTTPResponse 对象,HTTPResponse 对象常见的属性和方法如下:
geturl()
:返回 URLgetcode()
:返回状态码getheaders()
:返回全部响应头信息getheader(header)
:返回指定响应头信息info()
:返回响应头信息read()
:返回响应体(bytes 类型),通常需要使用decode('utf-8')
将其转化为 str 类型
from urllib import request
url = 'http://www.httpbin.org/get'
response = request.urlopen(url)
print(type(response))
print(response.geturl())
print(response.getcode())
print(response.getheaders())
print(response.getheader('Connection'))
print(response.info())
print(response.read().decode('utf-8'))
# 运行结果
>>> <class 'http.client.HTTPResponse'>
>>> http://www.httpbin.org/get
>>> 200
>>> [('Date', 'Tue, 08 Sep 2020 06:24:09 GMT'), ('Content-Type', 'application/json'), ('Content-Length', '283'), ('Connection', 'close'), ('Server', 'gunicorn/19.9.0'), ('Access-Control-Allow-Origin', '*'), ('Access-Control-Allow-Credentials', 'true')]
>>> close
>>> Date: Tue, 08 Sep 2020 10:11:28 GMT
Content-Type: application/json
Content-Length: 283
Connection: close
Server: gunicorn/19.9.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
>>> {
"args": {
},
"headers": {
"Accept-Encoding": "identity",
"Host": "www.httpbin.org",
"User-Agent": "Python-urllib/3.6",
"X-Amzn-Trace-Id": "Root=1-5f572389-909a186816689b48fdf37700"
},
"origin": "221.207.18.109",
"url": "http://www.httpbin.org/get"
}
下面是使用urlopen()方法快速抓取百度首页面的代码:
from urllib import request
url = 'http://www.baidu.com'
response = request.urlopen(url)
data = response.read()
with open("1.html", 'wb') as f:
f.write(data)
代码的逻辑很简单,首先定义一个目标网页URL,然后通过urlopen()方法访问目标网页,该方法模拟浏览器发送请求,返回一个 HTTPResponse 对象;再以read()方法读取响应内容;最后将内容保存到本地文件中。
2. Request对象
实际上,我们还可以给 urllib.request.open()
方法传入一个 Request 对象作为参数。为什么还需要使用 Request 对象呢?因为在上面的参数中我们无法指定 请求头部(headers),而headers在爬取一些设置的反爬的网页数据信息时,可以模拟浏览器访问这些网页,所以他在网络爬虫过程中起着重要的作用。
很多网站可能会首先检查请求头部中的 USER-AGENT
字段来判断该请求是否由网络爬虫程序发起。但是通过修改请求头部中的 USER_AGENT
字段,我们可以将爬虫程序伪装成浏览器,轻松绕过这一层检查。这里提供一个查找常用的 USER-AGENT 的网站:https://techblog.willshouse.com/2012/01/03/most-common-user-agents/
urllib.request.Request(url, data=None, headers={
}, origin_req_host=None, unverifiable=False, method=None)
参数说明如下:
url
:指定目标网站的 URLdata
:发送 POST 请求时提交的表单数据,默认为 Noneheaders
:发送请求时附加的请求头部,默认为 {}origin_req_host
:请求方的 host 名称或者 IP 地址,默认为 Noneunverifiable
:请求方的请求无法验证,默认为 Falsemethod
:指定请求方法,默认为 None
from urllib import request
url = 'http://www.httpbin.org/headers'
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
}
req = request.Request(url, headers=headers, method='GET')
res = request.urlopen(req)
html = res.read().decode('utf-8')
print(html)
# 运行结果:
{
"headers": {
"Accept-Encoding": "identity",
"Host": "www.httpbin.org",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36",
"X-Amzn-Trace-Id": "Root=1-5f575683-21240e2a6e35000b2793245b"
}
}
当我们以POST请求方式发送请求时: