我将分三篇文章介绍urllib的使用,这是第一篇。
目录(删除部分本文不介绍)
urllib 库的简介
发送请求(使用request模块)
异常处理(使用error模块)解析 url(使用parse模块)
1.urllib 库的简介
Python3内置了一个HTTP请求库—— urllib,通过它我们可以很方便的模拟http请求过程。这个库包含5个模块:
request:最基本的HTTP请求模块,可用来模拟发送请求的过程。
error:异常处理模块,用于捕获异常,防止程序意外终止。
parse:工具模块,提供了许多URL方法,比如拆分、合并等。
robotparser:识别网站的robot文件,判断哪些网站可以爬取。
response:Python官网未作详细介绍。
下面分别就前三个模块的使用给出详细介绍
2.发送请求
使用request模块我们可以很方便的发送http请求并得到响应。
2.1 urlopen()
想要抓取一个网页,我们可以使用urlopen函数,比如我们抓取百度实际上只需要两行代码:
>>>from urllib import request
>>>response = request.urlopen('https://www.baidu.com/')
response就包含了请求百度网页后的响应内容,我们可以查看一下它的类型:
>>>print(type(response))
<class 'http.client.HTTPResponse'>
可见response是http.client.HTTPResponse
类型。这个类型具有一些方法和属性,下面给出一些常用的方法和属性:
# 常用方法
>>>response.read() # 获取响应中的html代码
>>>response.getheaders() # 获取响应头中所有字段
>>>response.getheader('Server') # 响应头的特定字段 接受key 返回value
# 常用属性
>>>response.status # 获取响应的状态码 200表示正确返回 还有其他状态码比如404,405等
特别要指出的reponse的read方法返回的是html代码的字节流形式(即Python中的Bytes类型),并且对于一个response来说超出一次使用read方法得到的结果将为空。我们以百度的网页为例:
>>>response.read() # 第一次使用read方法 得到bytes类型的html代码
b'<html>\r\n<head>\r\n\t<script>\r\n\t\tlocation.replace(location.href.replace("https://","http://"));\r\n\t</script>\r\n</head>\r\n<body>\r\n\t<noscript><meta http-equiv="refresh" content="0;url=http://www.baidu.com/"></noscript>\r\n</body>\r\n</html>'
>>>response.read() # 第二次使用read方法 结果为空bytes
b''
使用read方法的时候需要注意上面两点。
到目前为止我们只为urlopen函数传入一个url就实现了最基本的GET请求。urlopen函数还提供了其他的参数帮助我们实现其他功能。我们再介绍一些。
- data参数
如果我们要以POST方式请求网页,我们就必须在调用urlopen函数时传入data参数。data参数必须是字节流格式,可以用bytes函数转换。下面是一个实例:
from urllib import parse
from urllib import request
url = 'http://httpbin.org/post'
# 构造data
dic = {'name':'li'}
dic_to_str = parse.urlencode(dic)
data = bytes(dic_to_str, encoding='utf8')
# 以POST方式请求网页
response = request.urlopen(url=url, data=data)
print(response.read())
--------------------------------
b'{"args":{},"data":"","files":{},"form":{"he":"123"},"headers":{"Accept-Encoding":"identity","Connection":"close","Content-Length":"6","Content-Type":"application/x-www-form-urlencoded","Host":"httpbin.org","User-Agent":"Python-urllib/3.7"},"json":null,"origin":"36.25.163.88","url":"http://httpbin.org/post"}\n'
在实例中,urlopen接收了一个data参数,这个参数是由一个字典转换而来的。parse.urlencode函数先将一个字典转化成字符串(确切的说这里将{'name':'li'}
转为了'name=li'
),然后再利用bytes函数将字符串转为字节流。
我们这里请求的站点比较特殊,他们可以测试我们的POST请求是否成功。注意到返回的html内容中有一项form,该字段的内容与我们传入的data内容一致,这就表示我们的POST请求成功了。有些人可能疑惑POST是什么,实际上,POST和GET都属于请求网页的一种方法,比较这两者,POST方式请求的时候需要我们给服务器多传入一个表单,而GET方式则只需要一个url即可。
- timeout参数
timeout参数决定请求网页时的超时时间,单位为秒。顾名思义就是当请求时间超出这个参数设置的值之后程序将会抛出一个异常,这可以避免网页长时间无响应造成的假死。来看实例:
# 设置超时时间很短 程序抛出异常
from urllib import request
>>>url = 'https://www.baidu.com/'
>>>request.urlopen(url, timeout=1) # 请求时间未超过timeout 正常返回
<http.client.HTTPResponse object at 0x00000241EA63A7F0>
>>>request.urlopen(url, timeout=0.01) # 请求时间超过timeout 抛出异常
...
raise URLError(err)
urllib.error.URLError: <urlopen error _ssl.c:1029: The handshake operation timed out>
通过trt-except语句我们可以在请求某网页太久之后跳过该网页,继续请求下面的网页:
from urllib import request
from urllib import error
try:
response = urllib.request.urlopen('https://www.baidu.com/', timeout=0.1)
except urllib.error.URLError as e:
print('TIME OUT', e)
-------------------------------
TIME OUT <urlopen error timed out>
urlopen函数还有一些参数,不过没有那么常用,我们可以在需要的时候查询文档。
2.2 利用Request()构造请求
尽管urlopen函数已经可以实现基本的请求了,但是它并不能构造一个完整的HTTP请求,而一些请求的字段对于爬虫来说又非常重要,比如请求头之中包含的User-Agent字段可以让爬虫模仿成浏览器,避免被服务器发觉而被封杀。为了构造更完整的请求,request模块提供了一个Request类。
Request类的API如下:
class urllib.request.Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None)
url是必选参数。
data参数只在需要传入表单时使用。它是一个bytes类型的参数。
headers参数接收一个字典,表示请求头。除了直接构造,还可以让构造完成的实例使用add_header方法添加。前面提到的User-Agent字段就属于请求头的内容。
origin_req_host 参数指的是请求方的 host 名称或者 IP 地址。
unverifiable 参数指的是这个请求是否是无法验证的,默认是False。意思就是说用户没有足够权限来选择接收这个请求的结果。例如我们请求一个 HTML 文档中的图片,但是我们没有自动抓取图像的权限,这时 unverifiable 的值就是 True。
method 参数是一个字符串,它用来指示请求使用的方法,比如GET,POST,PUT等等。
我们看一个例子:
from urllib import request, parse
url = 'http://httpbin.org/post'
headers = {
'User-Agent': 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)',
'Host': 'httpbin.org'
}
dict = {
'name': 'Germey'
}
data = bytes(parse.urlencode(dict), encoding='utf8')
req = request.Request(url=url, data=data, headers=headers, method='POST')
response = request.urlopen(req)
print(response.read().decode('utf-8'))
这个例子展示了由四个参数构造的Request对象req,请求时只需要把构造好的req传给urlopen函数就可以实现请求过程了。
参考资料:
[1] 《Python3 网络爬虫开发实战》 崔庆才
[2] https://docs.python.org/3/library/urllib.html#module-urllib urllib官方文档