爬虫基础 || 1.4 异常处理与链接解析

1.异常处理

在已经掌握了基本的爬虫技能,但是如果再发送请求中出现异常,如网络不好,请求被拒等情况,就可能出现报错而终止运行程序。

urllib的error模块定义了由request模块产生的异常。如果出现了问题,request模块便会爆出error模块中定义的异常。现在就使用error模块来处理各种异常。

1.1URLErrror

URLError类来自urllib的error模块,它继承自OSError类,是error异常模块的基类,由request模块所产生的异常都可以通过它来捕获。
它的一个重要属性reason,返回错误原因,有助于我们判断异常类型。

from urllib import request,error
try:
    request.urlopen('https://blog.csdn.net/Watson_Ashin/nothispage')
except error.URLError as e:
    print(e.reason)

==> Not Found

上面我们访问了一个不存在的地址,最终返回的是"Not Found",且程序并没有报错,就是说我们成功处理了异常,而程序依然再运行。

1.2HTTPERROR

它是URLError的子类,专门用来处理HTTP请求错误,比如认证请求失败等问题。它有如下三个属性:

  • code: 返回HETTP状态码,比如404表示网页不存在,500表示服务器内部错误等。
  • reason:返回错误原因
  • headers:返回请求头
from urllib import request,error
try:
    request.urlopen('https://blog.csdn.net/Watson_Ashin/nothispage')
except error.HTTPError as e:
    print(e.reason,e.code,e.headers,sep='\n')

==>输出如下

Not Found
404
Server: openresty
Date: Mon, 10 Feb 2020 07:36:31 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 15600
Connection: close
Vary: Accept-Encoding
Set-Cookie: uuid_tt_dd=10_18823982490-1581320191409-585554; Expires=Thu, 01 Jan 2025 00:00:00 GMT; Path=/; Domain=.csdn.net;
Set-Cookie: dc_session_id=10_1581320191409.605227; Expires=Thu, 01 Jan 2025 00:00:00 GMT; Path=/; Domain=.csdn.net;
ETag: "5e3b798b-3cf0"

上面的代码就返回了错误原因,错误码和请求头。因为URLError是HTTPerror的父类,所以根据规范应该先捕获子类,再捕获父类,所以代码如下:

from urllib import request,error
try:
    request.urlopen('https://blog.csdn.net/Watson_Ashin/nothispage')
except error.HTTPError as e:
    print(e.reason,e.code,e.headers,sep='\n')
except error.URLError as e:
    print(e.reason)
else:
    print('successful')

==>输出如下

Not Found
404
Server: openresty
Date: Mon, 10 Feb 2020 07:40:00 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 15600
Connection: close
Vary: Accept-Encoding
Set-Cookie: uuid_tt_dd=10_18823982490-1581320400576-704001; Expires=Thu, 01 Jan 2025 00:00:00 GMT; Path=/; Domain=.csdn.net;
Set-Cookie: dc_session_id=10_1581320400576.261739; Expires=Thu, 01 Jan 2025 00:00:00 GMT; Path=/; Domain=.csdn.net;
ETag: "5e3b798b-3cf0"

这样就可以做到先捕获HTTPError,获取它的错误状态码、原因、 headers 等。
如果不是 HTTPError 异常,就会捕获URLError异常,输出错误原因。
最后,用 else 来处理正常的逻辑。 这是一个饱满的的异常处理写法。

有时候,reason返回的是一个对象而非实例的情况下。

import socket
import urllib.request
import urllib.error
try:
    response = urllib.request.urlopen('https://www.baidu.com',timeout=0.01)
except urllib.error.URLError as e:
    print(type(e))
    if isinstance(e.reason,socket.timeout):
        print('TIME OUT')

==> 

<class 'urllib.error.URLError'>
TIME OUT

这里我们直接设置超时时间来强制抛出 timeout 异常。
可以发现, reason 属性的结果是socket.timeout 类。 所以,这里我们可以用 isinstance()方法来 判断它的类型,作出更详细的异常判断。
所以为了让爬虫程序更加稳健,这需要好好处理各种错误!

2.链接解析

urllib 库里还提供了parse模块,它定义了处理URL的标准接口,例如实现URL各部分的抽取、合并以及链接转换。 它支持如下协议的 URL 处理:file、fip、gopher、hdl、http、https、imap、mailto、mms、news、nntp、prospero、rsync、rtsp、rtspu、sftp、sip、sips、snews、svn、svn+ssh、telnet和 wais。、

其实就是构建URL,但是其实普通的字符串拼接也可以达到同样的效果,而使用parse模块可以高效得替换参数,另外一个就是解决编码问题。

2.1urlparse()

该方法可以实现URL的识别和分段

from urllib.parse import urlparse

result = urlparse('https://www.baidu.com/index.htrp;user?id=5#comment ')
print(type(result),result,sep='\n')
<class 'urllib.parse.ParseResult'>
ParseResult(scheme='https', netloc='www.baidu.com', path='/index.htrp', params='user', query='id=5', fragment='comment ')

可以看到,返回结果是一个ParseResult 类型的对象,它包含 6个部分,分别是scheme、 netloc、 path、 params 、 query 和 fragment。 观察一下该实例的 URL: http://www.baidu.com/index.html;user?id=S#comment ,urlparse()方法将其拆分成了 6个部分。 大体观察可以发现,解析时有特定的分隔符。 ://前面的就是scheme,代表协议; 第一个/符号前面便是netloc,即域名, 后面是 path,即访问路径; 分号;后面params,代表参数; 问号?后面是查询条件 query,一般用作GET类型的URL(以key-value形式); 井号#后面是锚点,用于直接定位页面内部的下拉位置。

2.urlunparse()

urlunparse()是urlparsed()的对立方法,他接受的参数是一个可迭代的对象,但是长度必须为6。其实就是简单的字符拼接,以构造URL。

from urllib.parse import urlunparse
data = ['htttp','www.baidu.com','index.html','user','a=6','comment']
print(urlunparse(data))

==>  htttp://www.baidu.com/index.html;user?a=6#comment

3.urlsplit()

这个方法和urlparse()方法非常相似,只不过他不再单独解析params这以部分,只返回五个结果。且可以通过索引获取相应的数值。

from urllib.parse import urlunparse
data = ['htttp','www.baidu.com','index.html','user','a=6','comment']
print(urlunparse(data))

==>SplitResult(scheme='http', netloc='www.baidu .com', path='/index.html;user', query='id=5', fragment='comment')

==>http

4.urlunsplit()

与urlunparse()类似,用于构造URL,且传入参数也需要为一个可迭代对象,但是长度必须为5,即params这个参数不传入。

5.urljoin()

有了 urlunparse()和 urlulisplit()方法,我们可以完成链接的合井,不过前提必须要有特定长度的对象,链接的每一部分都要清晰分开。 此外,生成链接还有另一个方法,那就是urljoin()方法。 我们可以提供一个基础链接(base_ur)作为第一个参数,将新的链接作为第二个参数,该方法会分析base_url的scheme、 netloc和path这3个内容并对新链接缺失的部分进行补充,最后返回结果。

from urllib.parse import urljoin
print(urljoin('http://www.baidu.com','FAQ.html'))
print(urljoin('http://www.baidu.com ','https://cuiqingcai.com/FAQ.html'))
print(urljoin('http://www.baidu.com/about.html', 'https://cuiqingcai.com/FAQ.html'))
print(urljoin('http://www.baidu.com/about.html','https://cuiqingcai.com/FAQ.html?question=2'))
print(urljoin('http://www.baidu.com?wd=abc','https://cuiqingcai.com/index.php'))
print(urljoin('http://www.baidu.com','?category=2#comment'))
print(urljoin('www.baidu.com','?category=2#comment'))
print(urljoin('www.baidu.com#comment','?category=2'))
http://www.baidu.com/FAQ.html
https://cuiqingcai.com/FAQ.html
https://cuiqingcai.com/FAQ.html
https://cuiqingcai.com/FAQ.html?question=2
https://cuiqingcai.com/index.php
http://www.baidu.com?category=2#comment
www.baidu.com?category=2#comment
www.baidu.com?category=2

base_url 提供了三项内容scheme,netloc和path。 如果这3项在新的链接里不存在, 就予以补充;如果新的链接存在,就使用新的链接的部分。而base_url中的 params,query和fragment 是不起作用的。

6.urlencode()

!!!画重点,这是一个常用的方法 urlencode(),它在构造 GET 请求参数的时候非常有用。!!!

from urllib.parse import urlencode 
params = { 'name':'watson',
          'age' : 22}
base_url = 'http://www.baidu.com?'
url = base_url + urlencode(params)
print(url)

==>http://www.baidu.com?name=watson&age=22

这里参数通过字典来传递给urlencode()方法,然后将其序列化成URL(这里只能用于GET请求),这在爬取多页面的是适合会经常用到,但是其实底层就是简单的字符串替代逻辑。

7.parse_qs() 与parse_qsl()

有了序列化,必然就有反序列化。 如果我们有一串 GET 请求参数,利用 parse_qs()或者parse_qsl()方法,就可以将其转回字典或者元组组成的列表。

from urllib.parse import parse_qs 
query= 'name=germey&age=22' 
print(parse_qs(query)) 

==>{'name': ['germey'], 'age': ['22']}

from urllib.parse import parse_qsl 
query= 'name=germey&age=22' 
print(parse_qsl(query)) 

==>[('name', 'germey'), ('age', '22')]

8.quote()

该方法可以将内容转化为 URL 编码的格式。 URL 中带有中文参数时,有时可能会导致乱码的问题,此时用这个方法可以将中文转化为 URL 编码

from urllib.parse import quote 
keyword ='哪吒' 
url = 'https://www.baidu.com/s?wd='+ quote(keyword) 
print(url) 

==>https://www.baidu.com/s?wd=%E5%93%AA%E5%90%92

这里在搜索类爬虫的适合经常回用到该方法,除了淘宝网,在旅游类也是经常需要字符转化。

9.unquote()

那么既然又编码就要解码,unquote就是将URL编码字符解码。

from urllib.parse import unquote
print(unquote('%E5%93%AA%E5%90%92'))

==> 哪吒

发布了17 篇原创文章 · 获赞 12 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Watson_Ashin/article/details/104295349
1.4
今日推荐