网络是十分复杂的。网页数据格式不友好,网站服务器宕机,目标数据的标签找不到,都
是很麻烦的事情。网络数据采集最痛苦的遭遇之一,就是爬虫运行的时候你洗洗睡了,梦
想着明天一早数据就都会采集好放在数据库里,结果第二天醒来,你看到的却是一个因某
种数据格式异常导致运行错误的爬虫,在前一天当你不再盯着屏幕去睡觉之后,没过一会
儿爬虫就不再运行了。那个时候,你可能想骂发明互联网(以及那些奇葩的网络数据格
式)的人,但是你真正应该斥责的人是你自己,为什么一开始不估计可能会出现的异常!
html = urlopen("http://www.pythonscraping.com/pages/page1.html")
这行代码主要可能会发生两种异常:
? 网页在服务器上不存在(或者获取页面的时候出现错误)
? 服务器不存在
第一种异常发生时,程序会返回HTTP 错误。HTTP 错误可能是“404 Page Not Found”“500
Internal Server Error”等。所有类似情形,urlopen 函数都会抛出“HTTPError”异常。我们
可以用下面的方式处理这种异常:
try: html = urlopen("http://www.pythonscraping.com/pages/page1.html") except HTTPError as e: print(e)
# 返回空值,中断程序,或者执行另一个方案
else:
# 程序继续。注意:如果你已经在上面异常捕捉那一段代码里返回或中断(break),
# 那么就不需要使用else语句了,这段代码也不会执行
如果程序返回HTTP 错误代码,程序就会显示错误内容,不再执行else 语句后面的代码。
如果服务器不存在(就是说链接http://www.pythonscraping.com/ 打不开,或者是URL 链接
写错了),urlopen 会返回一个None 对象。这个对象与其他编程语言中的null 类似。我们
可以增加一个判断语句检测返回的html 是不是None:
if html is None: print("URL is not found") else: # 程序继续
当然,即使网页已经从服务器成功获取,如果网页上的内容并非完全是我们期望的那样,
仍然可能会出现异常。每当你调用BeautifulSoup 对象里的一个标签时,增加一个检查条件
保证标签确实存在是很聪明的做法。如果你想要调用的标签不存在,BeautifulSoup 就会返
初见网络爬虫 | 9
回None 对象。不过,如果再调用这个None 对象下面的子标签,就会发生AttributeError
错误。
下面这行代码(nonExistentTag 是虚拟的标签,BeautifulSoup 对象里实际没有)
print(bsObj.nonExistentTag)
会返回一个None 对象。处理和检查这个对象是十分必要的。如果你不检查,直接调用这个
None 对象的子标签,麻烦就来了。如下所示。
print(bsObj.nonExistentTag.someTag)
这时就会返回一个异常:
AttributeError: ‘NoneType‘ object has no attribute ‘someTag‘
那么我们怎么才能避免这两种情形的异常呢?最简单的方式就是对两种情形进行检查:
try: badContent = bsObj.nonExistingTag.anotherTag except AttributeError as e: print("Tag was not found") else: if badContent == None: print ("Tag was not found") else: print(badContent)
初看这些检查与错误处理的代码会觉得有点儿累赘,但是,我们可以重新简单组织一下代
码,让它变得不那么难写(更重要的是,不那么难读)。例如,下面的代码是上面爬虫的
另一种写法:
from urllib.request import urlopen from urllib.error import HTTPError from bs4 import BeautifulSoup def getTitle(url): try: html = urlopen(url) except HTTPError as e: return None try: bsObj = BeautifulSoup(html.read()) title = bsObj.body.h1 except AttributeError as e: return None return title title = getTitle("http://www.pythonscraping.com/pages/page1.html") if title == None: print("Title could not be found") else: print(title)
在这个例子中,我们创建了一个getTitle 函数,可以返回网页的标题,如果获取网页
的时候遇到问题就返回一个None 对象。在getTitle 函数里面,我们像前面那样检查了
HTTPError,然后把两行BeautifulSoup 代码封装在一个try 语句里面。这两行中的任何一
行有问题,AttributeError 都可能被抛出(如果服务器不存在,html 就是一个None 对象,
html.read() 就会抛出AttributeError)。其实,我们可以在try 语句里面放任意多行代码,
或者放一个在任意位置都可以抛出AttributeError 的函数。