参考博客原址:https://blog.csdn.net/bo_mask/article/details/76067790
在Python2版本中,有urllib和urlib2两个库可以用来实现request的发送。而在Python3中,已经不存在urllib2这个库了,统一为urllib。Python3 urllib库官方链接
https://docs.python.org/3/library/urllib.html
urllib.request,urllib.error,urllib.parse,urllib.robotparser
urllib.robotparse用来解析页面的robots.txt文件
下面会对它们一一进行详细的介绍。
urllib.request.urlopen()基本使用
urllib.request 模块提供了最基本的构造 HTTP 请求的方法,利用它可以模拟浏览器的一个请求发起过程,同时它还带有处理 authenticaton (授权验证), redirections (重定向), cookies (浏览器Cookies)以及其它内容。好,那么首先我们来感受一下它的强大之处,我们百度为例,我们来把这个网页抓下来。
-
import urllib.request
-
-
response = urllib.request.urlopen(
“https://www.baidu.com”)
-
print(response.read().decode(
“utf-8”))
真正的代码只有两行,我们便完成了百度的抓取,输出了网页的源代码,得到了源代码之后呢?你想要的链接、图片地址、文本信息不就都可以提取出来了吗?接下来我们看下它返回的到底是什么,利用 type 函数输出 response 的类型。
-
>>>
import urllib.request
-
>>> response = urllib.request.urlopen(
“https://www.baidu.com”)
-
>>> print(type(response))
-
<
class ‘http.client.HTTPResponse’>
通过输出结果可以发现它是一个 HTTPResposne 类型的对象,它主要包含的方法有 read() 、 readinto() 、getheader(name) 、 getheaders() 、 fileno() 等函数和 msg 、 version 、 status 、 reason 、 debuglevel 、 closed 等属性。 得到这个对象之后,赋值为 response ,然后就可以用 response 调用这些方法和属性,得到返回结果的一系列信息。例如 response.read() 就可以得到返回的网页内容, response.status 就可以得到返回结果的状态码,如200代表请求成功,404代表网页未找到等。
下面再来一个实例感受一下:
-
>>>
import urllib.request
-
>>> response = urllib.request.urlopen(
“https://www.baidu.com”)
-
>>> print(response.status)
-
200
-
>>> print(response.getheaders())
-
[(
‘Accept-Ranges’,
‘bytes’), (
‘Cache-Control’,
‘no-cache’), (
‘Content-Length’,
‘227’), (
‘Content-Type’,
‘text/html’), (
‘Date’,
‘Tue, 25 Jul 2017 06:36:40 GMT’), (
‘Last-Modified’,
‘Wed, 28 Jun 2017 02:16:00 GMT’), (
‘P3p’,
‘CP=" OTI DSP COR IVA OUR IND COM "’), (
‘Pragma’,
‘no-cache’), (
‘Server’,
‘BWS/1.1’), (
‘Set-Cookie’,
‘BD_NOT_HTTPS=1; path=/; Max-Age=300’), (
‘Set-Cookie’,
‘BIDUPSID=BEF13521D9F33BE4108EA36C07303743; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com’), (
‘Set-Cookie’,
‘PSTM=1500964600; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com’), (
‘Strict-Transport-Security’,
‘max-age=0’), (
‘X-Ua-Compatible’,
‘IE=Edge,chrome=1’), (
‘Connection’,
‘close’)]
-
>>> print(response.getheader(
“Server”))
-
BWS/
1.1
利用以上最基本的 urlopen() 方法,我们可以完成最基本的简单网页的 GET 请求抓取。
如果我们想给链接传递一些参数该怎么实现呢?我们首先看一下 urlopen() 函数的API。
urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)
data 参数是可选的,如果要添加 data ,它要是字节流编码格式的内容,即 bytes 类型,通过 bytes() 函数可以进行转化,另外如果你传递了这个 data 参数,它的请求方式就不再是 GET 方式请求,而是 POST 。
-
# coding=utf-8
-
import urllib.parse
-
import urllib.request
-
data = bytes(urllib.parse.urlencode({
‘word’:
‘hello’}), encoding=
-
‘utf8’)
-
response = urllib.request.urlopen(
‘http://httpbin.org/post’, dat
-
a=data)
-
print(response.read())
在这里我们传递了一个参数 word ,值是 hello 。它需要被转码成 bytes (字节流)类型。其中转字节流采用了 bytes() 方法,第一个参数需要是 str (字符串)类型,需要用 urllib.parse.urlencode() 方法来将参数字典转化为字符串。第二个参数指定编码格式,在这里指定为 utf8 。
-
{
-
“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.5”
-
},
-
“json”: null,
-
“origin”:
“123.124.23.253”,
-
“url”:
“http://httpbin.org/post”
-
}
timeout参数
timeout 参数可以设置超时时间,单位为秒,意思就是如果请求超出了设置的这个时间还没有得到响应,就会抛出异常,如果不指定,就会使用全局默认时间。它支持 HTTP 、 HTTPS 、 FTP 请求。
下面来用一个实例感受一下:
-
>>>
import urllib.request
-
>>> response = urllib.request.urlopen(
“http://httpbin.org/get”,timeout=
1)
-
>>> print(response.read())
-
During handling of the above exception, another exception occurr
-
ed:
-
Traceback (most recent call last): File
“/var/py/python/urllibte
-
st.py”, line
4,
in
response = urllib.request.urlopen(
‘h
-
ttp://httpbin.org/get’, timeout=
1)
-
…
-
urllib.error.URLError:
因此我们可以通过设置这个超时时间来控制一个网页如果长时间未响应就跳过它的抓取,利用 try,except 语句就可以实现这样的操作。
-
import urllib.request
-
import socket
-
import urllib.error
-
try:
-
response = urllib.request.urlopen(
‘http://httpbin.org/get’,timeout=
0.1)
-
except urllib.error.URLError
as e:
-
if isinstance(e.reason, socket.timeout):
-
print(
“Time out!”)
运行结果如下:
Time out!
常理来说,0.1秒内基本不可能得到服务器响应,因此输出了 TIME OUT 的提示。这样,我们可以通过设置 timeout 这个参数来实现超时处理,有时还是很有用的。
其他参数
还有 context 参数,它必须是 ssl.SSLContext 类型,用来指定 SSL 设置。cafile 和 capath 两个参数是指定CA证书和它的路径,这个在请求 HTTPS 链接时会有用。
cadefault 参数现在已经弃用了,默认为 False 。
以上讲解了 url.request.urlopen() 方法的用法,通过这个最基本的函数可以完成简单的请求和网页抓取,如需详细了解,可以参见官方文档。
https://docs.python.org/3/library/urllib.request.html
由上我们知道利用 urlopen() 方法可以实现最基本的请求发起,但这几个简单的参数并不足以构建一个完整的请求,如果请求中需要加入 headers 等信息,我们就可以利用更强大的 Request 类来构建一个请求。
首先我们用一个实例来感受一下 Request 的用法:
-
import urllib.request
-
-
request =urllib.request.Request(
“https://www.baidu.com”)
-
response = urllib.request.urlopen(request)
-
print(response.read().decode(
“utf-8”))
class urllib.request.Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None)
data 参数如果要传必须传 bytes (字节流)类型的,如果是一个字典,可以先用 urllib.parse.urlencode() 编码。
headers 参数是一个字典,你可以在构造 Request 时通过 headers 参数传递,也可以通过调用 Request 对象的 add_header() 方法来添加请求头。请求头最常用的用法就是通过修改 User-Agent 来伪装浏览器,默认的 User-
Agent 是 Python-urllib ,你可以通过修改它来伪装浏览器,比如要伪装火狐浏览器,你可以把它设置为 Mozilla/5.0 (X11; U; Linux i686)Gecko/20071127 Firefox/2.0.0.11
origin_req_host 指的是请求方的 host 名称或者 IP 地址。
unverifiable 指的是这个请求是否是无法验证的,默认是 False 。意思就是说用户没有足够权限来选择接收这个请求的结果。例如我们请求一个HTML文档中的图片,但是我们没有自动抓取图像的权限,这时 unverifiable 的值就是 True 。
method 是一个字符串,它用来指示请求使用的方法,比如 GET , POST , PUT 等等。
下面我们传入多个参数构建一个 Request 来感受一下:
-
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”))
运行结果如下:
-
{
-
“args”: {},
-
“data”:
“”,
-
“files”: {},
-
“form”: {
-
“name”:
“Germey”
-
},
-
“headers”: {
-
“Accept-Encoding”:
“identity”,
-
“Connection”:
“close”,
-
“Content-Length”:
“11”,
-
“Content-Type”:
“application/x-www-form-urlencoded”,
-
“Host”:
“httpbin.org”,
-
“User-Agent”:
“Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)”
-
},
-
“json”: null,
-
“origin”:
“1.85.221.5”,
-
“url”:
“http://httpbin.org/post”
-
}
另外 headers 也可以用 add_header() 方法来添加。
req = request.Request(url=url, data=data, method=‘POST’)
req.add_header(‘User-Agent’, ‘Mozilla/4.0 (compatible; MSIE 5.5;Windows NT)’)
如此一来,我们就可以更加方便地构造一个 Request ,实现请求的发送。
大家有没有发现,在上面的过程中,我们虽然可以构造 Request ,但是一些更高级的操作,比如 Cookies 处理,代理该怎样来设置?
接下来就需要更强大的工具 Handler 登场了。
简而言之你可以把它理解为各种处理器,有专门处理登录验证的,有处理 Cookies 的,有处理代理设置的,利用它们我们几乎可以做到任何 HTTP 请求中所有的事情。
首先介绍下 urllib.request.BaseHandler ,它是所有其他 Handler 的父类,它提供了最基本的 Handler 的方法,例
如 default_open() 、 protocol_request() 等。
接下来就有各种 Handler 类继承这个 BaseHandler ,列举如下:
https://docs.python.org/3/library/urllib.request.html#urllib.request.BaseHandler
它们怎么来使用,不用着急,下面会有实例为你演示。
另外一个比较重要的就是 OpenerDirector ,我们可以称之为 Opener ,我们之前用过 urllib.request.urlopen() 这个方法,实际上它就是一个 Opener 。
那么为什么要引入 Opener 呢?因为我们需要实现更高级的功能,之前我们使用的 Request 、 urlopen() 相当于类库为你封装好了极其常用的请求方法,利用它们两个我们就可以完成基本的请求,但是现在不一样了,我们需要实现更高级的功能,所以我们需要深入一层,使用更上层的实例来完成我们的操作。所以,在这里我们就用到了比调用 urlopen() 的对象的更普遍的对象,也就是 Opener 。
Opener 可以使用 open() 方法,返回的类型和 urlopen() 如出一辙。那么它和 Handler 有什么关系?简而言之,就是利用 Handler 来构建 Opener 。
认证
我们先用一个实例来感受一下:
-
import urllib.request
-
auth_handler = urllib.request.HTTPBasicAuthHandler()
-
auth_handler.add_password(realm=
‘PDQ Application’,
-
uri=
‘https://mahler:8092/site-updates.py’,
-
user=
‘klem’,
-
passwd=
‘kadidd!ehopper’)
-
opener = urllib.request.build_opener(auth_handler)
-
urllib.request.install_opener(opener)
-
urllib.request.urlopen(
‘http://www.example.com/login.html’)
如果添加代理,可以这样做:
-
import urllib.request
-
proxy_handler = urllib.request.ProxyHandler({
-
‘http’:
‘http://218.202.111.10:80’,
-
‘https’:
‘https://180.250.163.34:8888’
-
})
-
opener = urllib.request.build_opener(proxy_handler)
-
response = opener.open(
‘https://www.baidu.com’)
-
print(response.read())
在这里使用了 ProxyHandler , ProxyHandler 的参数是一个字典,key是协议类型,比如 http 还是 https 等,value是代理链接,可以添加多个代理。
然后利用 build_opener() 方法利用这个 Handler 构造一个 Opener ,然后发送请求即可。
我们先用一个实例来感受一下怎样将网站的 Cookie 获取下来。
-
import http.cookiejar, urllib.request
-
cookie = http.cookiejar.CookieJar()
-
handler = urllib.request.HTTPCookieProcessor(cookie)
-
opener = urllib.request.build_opener(handler)
-
response = opener.open(
‘http://www.baidu.com’)
-
for item
in cookie:
-
print(item.name+
”="+item.value)
首先我们必须声明一个 CookieJar 对象,接下来我们就需要利用 HTTPCookieProcessor 来构建一个 handler ,最后利用 build_opener 方法构建出 opener ,执行 open() 即可。
运行结果如下:
-
BAIDUID=
2E65A683F8A8BA3DF521469DF8EFF1E1:FG=
1
-
BIDUPSID=
2E65A683F8A8BA3DF521469DF8EFF1E1
-
H_PS_PSSID=
20987_1421_18282_17949_21122_17001_21227_21189_21161_20927
-
PSTM=
1474900615
-
BDSVRTM=
0
-
BD_HOME=
0
可以看到输出了每一条 Cookie 的名称还有值。
-
filename =
'cookie.txt’
-
cookie = http.cookiejar.MozillaCookieJar(filename)
-
handler = urllib.request.HTTPCookieProcessor(cookie)
-
opener = urllib.request.build_opener(handler)
-
response = opener.open(
‘http://www.baidu.com’)
-
cookie.save(ignore_discard=
True, ignore_expires=
True)
这时的 CookieJar 就需要换成 MozillaCookieJar ,生成文件时需要用到它,它是 CookieJar 的子类,可以用来处理 Cookie 和文件相关的事件,读取和保存 Cookie ,它可以将 Cookie 保存成 Mozilla 型的格式。内容如下:
-
# Netscape HTTP Cookie File
-
# http://curl.haxx.se/rfc/cookie_spec.html
-
# This is a generated file! Do not edit.
-
.baidu.com TRUE / FALSE
3622386254 BAIDUID
05A
-
E39B5F56C1DEC474325CDA522D44F:FG=
1
-
.baidu.com TRUE / FALSE
3622386254 BIDUPSID
05
-
AE39B5F56C1DEC474325CDA522D44F
-
.baidu.com TRUE / FALSE H_PS_PSSID
19638_1453
-
_17710_18240_21091_18560_17001_21191_21161
-
.baidu.com TRUE / FALSE
3622386254 PSTM
147490
-
2606
-
www.baidu.com FALSE / FALSE BDSVRTM
0
-
www.baidu.com FALSE / FALSE BD_HOME
0
cookie = http.cookiejar.LWPCookieJar(filename)
-
#LWP-Cookies-2.0
-
Set-Cookie3: BAIDUID=
“0CE9C56F598E69DB375B7C294AE5C591:FG=1”; path=
"/"; domain=
".baidu.com"; path_spec; domain_dot; expires=
“208
-
4-10-14 18:25:19Z”; version=
0
-
Set-Cookie3: BIDUPSID=
0CE9C56F598E69DB375B7C294AE5C591; path=
"/"; domain=
".baidu.com"; path_spec; domain_dot; expires=
“2084-10-1
-
4 18:25:19Z”; version=
0
-
Set-Cookie3: H_PS_PSSID=
20048_1448_18240_17944_21089_21192_21161_20929; path=
"/"; domain=
".baidu.com"; path_spec; domain_dot; di
-
scard; version=
0
-
Set-Cookie3: PSTM=
1474902671; path=
"/"; domain=
".baidu.com"; path_spec; domain_dot; expires=
“2084-10-14 18:25:19Z”; version=
0
-
Set-Cookie3: BDSVRTM=
0; path=
"/"; domain=
“www.baidu.com”; path_spec; discard; version=
0
-
Set-Cookie3: BD_HOME=
0; path=
"/"; domain=
“www.baidu.com”; path_spec; discard; version=
0
-
-
-
cookie = http.cookiejar.LWPCookieJar()
-
cookie.load(
‘cookie.txt’, ignore_discard=
True, ignore_expires=
True)
-
handler = urllib.request.HTTPCookieProcessor(cookie)
-
opener = urllib.request.build_opener(handler)
-
response = opener.open(
‘http://www.baidu.com’)
-
print(response.read().decode(
‘utf-8’))
前提是我们首先利用上面的方式生成了 LWPCookieJar 格式的 Cookie ,然后利用 load() 方法,传入文件名称,后面同样的方法构建 handler 和 opener 即可。
运行结果正常输出百度网页的源代码。
好,通过如上用法,我们可以实现绝大多数请求功能的设置了。
发出请求之后,如果遇到异常怎么办?在下一节,我们会讲解一下有关异常处理的
流程。