什么是Urllib?
Urllib是python内置的HTTP请求库,包括以下模块:
- urllib.request 请求模块
- urllib.error 异常处理模块
- urllib.parse url解析模块
- robots.txt解析模块
1)urllib.request 请求模块
模拟浏览器发起一个 HTTP 请求,我们需要用到 urllib.request 模块。urllib.request 的作用不仅仅是发起请求, 还能获取请求返回结果。发起请求,单靠 urlopen() 方法就可以叱咤风云。我们先看下 urlopen() 的 API:
urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)
url
:第一个参数 String 类型的地址或者变量data
:是 bytes 类型的内容,可通过 bytes()函数转为化字节流。它也是可选参数。使用 data 参数,请求方式变成以 POST 方式提交表单。使用标准格式是application/x-www-form-urlencodedtimeout
:参数是用于设置请求超时时间。单位是秒。cafile
和capath
:代表 CA 证书和 CA 证书的路径。如果使用HTTPS则需要用到。context
:参数必须是ssl.SSLContext类型,用来指定SSL设置cadefault
:参数已经被弃用,可以不用管了。
该方法也可以单独传入urllib.request.Request对象
该函数返回结果是一个http.client.HTTPResponse对象。
1.1 简单抓取网页
我们使用 urllib.request.urlopen() 去请求百度,并获取到它页面的源代码。
import urllib.request
url = "http://www.baidu.com"
response = urllib.request.urlopen(url)
html = response.read() # 获取到页面的源代码
print(html.decode('utf-8')) # 转化为 utf-8 编码
1.2 设置请求超时
有些请求可能因为网络原因无法得到响应。因此,我们可以手动设置超时时间。当请求超时,我们可以采取进一步措施,例如选择直接丢弃该请求或者再请求一次。
import urllib.request
url = "http://www.baidu.com"
response = urllib.request.urlopen(url, timeout=1)
print(response.read().decode('utf-8'))
1.3 使用 data 参数提交数据
在请求某些网页时需要携带一些数据,我们就需要使用到 data 参数。
import urllib.parse
import urllib.request
url = "http://127.0.0.1:8000/book"
params = {
'name':'浮生六记',
'author':'沈复'
}
data = bytes(urllib.parse.urlencode(params), encoding='utf8')
response = urllib.request.urlopen(url, data=data)
print(response.read().decode('utf-8'))
params 需要被转码成字节流,而 params 是一个字典。我们需要使用urllib.parse.urlencode() 将字典转化为字符串。再使用 bytes() 转为字节流。最后使用 urlopen() 发起请求,请求是模拟用 POST 方式提交表单数据。
1.4 使用 Request
由上我们知道利用 urlopen() 方法可以发起简单的请求。但这几个简单的参数并不足以构建一个完整的请求,如果请求中需要加入headers(请求头)、指定请求方式等信息,我们就可以利用更强大的Request类来构建一个请求。
Request 的构造方法:
urllib.request.Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None)
url
参数是请求链接,这个是必传参数,其他的都是可选参数。data
参数跟 urlopen() 中的 data 参数用法相同。headers
参数是指定发起的 HTTP 请求的头部信息。headers 是一个字典。它除了在 Request 中添加,还可以通过调用 Reques t实例的 add_header() 方法来添加请求头。origin_req_host
参数指的是请求方的 host 名称或者 IP 地址。unverifiable
参数表示这个请求是否是无法验证的,默认值是False。意思就是说用户没有足够权限来选择接收这个请求的结果。例如我们请求一个HTML文档中的图片,但是我们没有自动抓取图像的权限,我们就要将 unverifiable 的值设置成 True。
method
参数指的是发起的 HTTP 请求的方式,有 GET、POST、DELETE、PUT等
1.4.1 简单使用 Request
使用 Request 伪装成浏览器发起 HTTP 请求。如果不设置 headers 中的User-Agent,默认的User-Agent是Python-urllib/3.5。可能一些网站会将该请求拦截,所以需要伪装成浏览器发起请求。
User-Agent 设置为 Chrome 浏览器:
import urllib.request
url = "http://tieba.baidu.com/"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'
}
request = urllib.request.Request(url=url, headers=headers)
response = urllib.request.urlopen(request)
print(response.read().decode('utf-8'))
1.4.2 Request 高级用法
如果我们需要在请求中添加代理、处理请求的 Cookies,我们需要用到Handler
和OpenerDirector
。
Handler
中文意思是处理者、处理器。 Handler 能处理请求(HTTP、HTTPS、FTP等)中的各种事情。它的具体实现是这个类 urllib.request.BaseHandler。它是所有的 Handler 的基类,其提供了最基本的Handler的方法,例如default_open()、protocol_request()等。
继承 BaseHandler 有很多个,列举几个比较常见的类:
ProxyHandler
:为请求设置代理HTTPCookieProcessor
:处理 HTTP 请求中的 CookiesHTTPDefaultErrorHandle
r:处理 HTTP 响应错误。HTTPRedirectHandler
:处理 HTTP 重定向。HTTPPasswordMgr
:用于管理密码,它维护了用户名密码的表。HTTPBasicAuthHandler
:用于登录认证,一般和 HTTPPasswordMgr 结合使用。
OpenerDirector
我们可以称之为 Opener。我们之前用过 urlopen() 这个方法,实际上它就是 urllib 为我们提供的一个Opener。那 Opener 和 Handler 又有什么关系?opener 对象是由 build_opener(handler) 方法来创建出来 。我们需要创建自定义的 opener,就需要使用install_opener(opener)
方法。值得注意的是,install_opener 实例化会得到一个全局的 OpenerDirector 对象。
1.5 使用代理
有些网站做了浏览频率限制。如果我们请求该网站频率过高。该网站会被封 IP,禁止我们的访问。所以我们需要使用代理来突破这“枷锁”。
例:为 HTTP 请求设置代理
import urllib.request
url = "http://www.baidu.com/"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'
}
proxy_handler = urllib.request.ProxyHandler({
'http': 'web-proxy.oa.com:8080',
'https': 'web-proxy.oa.com:8080'
})
opener = urllib.request.build_opener(proxy_handler)
urllib.request.install_opener(opener)
request = urllib.request.Request(url=url, headers=headers)
response = urllib.request.urlopen(request)
print(response.read().decode('utf-8'))
1.6 认证登录
有些网站需要携带账号和密码进行登录之后才能继续浏览网页。碰到这样的网站,我们需要用到认证登录。步骤如下:
- 使用HTTPPasswordMgrWithDefaultRealm() 实例化一个账号密码管理对象;
- 使用 add_password() 函数添加账号和密码;
- 接着使用 HTTPBasicAuthHandler() 得到 hander;
- 使用 build_opener() 获取 opener 对象;
- 使用 opener 的 open() 函数发起请求。
例:携带账号和密码请求登录百度贴吧,代码如下:
import urllib.request
url = "http://tieba.baidu.com/"
user = 'user' #账号
password = 'password' #密码
pwdmgr = urllib.request.HTTPPasswordMgrWithDefaultRealm() #实例化一个账号密码管理对象
pwdmgr.add_password(None,url ,user ,password) #对实例化添加账号和密码
auth_handler = urllib.request.HTTPBasicAuthHandler(pwdmgr)
opener = urllib.request.build_opener(auth_handler)
response = opener.open(url)
print(response.read().decode('utf-8'))
1.7 Cookies设置
如果请求的页面每次需要身份验证,我们可以使用 Cookies 来自动登录,免去重复登录验证的操作。步骤:
- 获取 Cookies 需要使用
http.cookiejar.CookieJar()
实例化一个 Cookies 对象。 - 用
urllib.request.HTTPCookieProcessor
构建出 handler 对象。 - 使用 opener 的 open() 函数即可。
例:获取请求百度的 Cookies 并保存到文件中,代码如下:
import http.cookiejar
import urllib.request
url = "http://www.baidu.com/"
fileName = 'cookie.txt'
cookie = http.cookiejar.CookieJar()
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open(url)
f = open(fileName,'a')
for item in cookie:
f.write(item.name+" = "+item.value+'\n')
f.close()
1.8 HTTPResponse
从上面的例子可知, 使用 urllib.request.urlopen() 或者 opener.open(url) 返回结果是一个 http.client.HTTPResponse 对象。它具有 msg、version、status、reason、debuglevel、closed等属性以及read()
、readinto()
、getheader(name)
、getheaders()
、fileno()
等函数。
2)urllib.error 异常处理模块
发起请求难免会出现各种异常,我们需要对异常进行处理,这样会使得程序比较人性化。
异常处理主要用到两个类,urllib.error.URLError
和urllib.error.HTTPError
。
2.1 URLError
URLError
urllib.error 异常类的基类, 可以捕获由urllib.request 产生的异常。它具有一个属性reason,即返回错误的原因。
URLError可能产生的原因:
- 网络无连接,即本机无法上网
- 连接不到特定的服务器
- 服务器不存在
捕获 URL 异常的示例代码:
import urllib.request
import urllib.error
url = "http://www.google.com"
try:
response = urllib.request.urlopen(url)
except urllib.error.URLError as e:
print(e.reason)
2.2 HTTPError
HTTPError
是 URLError 的子类,专门处理 HTTP 和 HTTPS 请求的错误。它具有三个属性。
- code:HTTP 请求返回的状态码。
- renson:与父类用法一样,表示返回错误的原因。
- headers:HTTP 请求返回的响应头信息。
例:获取 HTTP 异常的示例代码, 输出了错误状态码、错误原因、服务器响应头
import urllib.request
import urllib.error
url = "http://www.google.com"
try:
response = urllib.request.urlopen(url)
except urllib.error.HTTPError as e:
print('code: ' + e.code + '\n')
print('reason: ' + e.reason + '\n')
print('headers: ' + e.headers + '\n')
其他不能处理的,urlopen会产生一个HTTPError,对应相应的状态吗,HTTP状态码表示HTTP协议所返回的响应的状态。下面将状态码归结如下
100:继续 客户端应当继续发送请求。客户端应当继续发送请求的剩余部分,或者如果请求已经完成,忽略这个响应。
101: 转换协议 在发送完这个响应最后的空行后,服务器将会切换到在Upgrade 消息头中定义的那些协议。只有在切换新的协议更有好处的时候才应该采取类似措施。
102:继续处理 由WebDAV(RFC 2518)扩展的状态码,代表处理将被继续执行。
200:请求成功 处理方式:获得响应的内容,进行处理
201:请求完成,结果是创建了新资源。新创建资源的URI可在响应的实体中得到 处理方式:爬虫中不会遇到
202:请求被接受,但处理尚未完成 处理方式:阻塞等待
204:服务器端已经实现了请求,但是没有返回新的信 息。如果客户是用户代理,则无须为此更新自身的文档视图。 处理方式:丢弃
300:该状态码不被HTTP/1.0的应用程序直接使用, 只是作为3XX类型回应的默认解释。存在多个可用的被请求资源。 处理方式:若程序中能够处理,则进行进一步处理,如果程序中不能处理,则丢弃
301:请求到的资源都会分配一个永久的URL,这样就可以在将来通过该URL来访问此资源 处理方式:重定向到分配的URL
302:请求到的资源在一个不同的URL处临时保存 处理方式:重定向到临时的URL
304:请求的资源未更新 处理方式:丢弃
400:非法请求 处理方式:丢弃
401:未授权 处理方式:丢弃
403:禁止 处理方式:丢弃
404:没有找到 处理方式:丢弃
500:服务器内部错误 服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。一般来说,这个问题都会在服务器端的源代码出现错误时出现。
501:服务器无法识别 服务器不支持当前请求所需要的某个功能。当服务器无法识别请求的方法,并且无法支持其对任何资源的请求。
502:错误网关 作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应。
503:服务出错 由于临时的服务器维护或者过载,服务器当前无法处理请求。这个状况是临时的,并且将在一段时间以后恢复。
HTTPError实例产生后会有一个code属性,这就是是服务器发送的相关错误号。
写一个例子来感受一下,捕获的异常是HTTPError,它会带有一个code属性,就是错误代号,另外我们又打印了reason属性,这是它的父类URLError的属性。
我们知道,HTTPError的父类是URLError,根据编程经验,父类的异常应当写到子类异常的后面,如果子类捕获不到,那么可以捕获父类的异常,所以上述的代码可以这么写
import urllib.request
import urllib.error
req = urllib.request.Request('http://www.google.com/123')
try:
urllib.request.urlopen(req)
except urllib.error.HTTPError as e:
print (e.code)
except urllib.error.URLError as e:
print (e.reason)
else:
print ("OK")