连接可靠性
网页是复杂的。数据的格式差,随着网站的宕机,可使用的标签也会消失。在网络爬虫中最令人感到挫折的经历之一就是:让爬虫运行,睡觉,梦到第二天你在你的数据库中看到了所有的数据,然而,现实是---仅仅返现了爬虫遇到了一个意想不到的数据格式的小错误,在你关闭电脑屏幕之后不久就停止了任务的执行。在相类似的情形下,你可能是对构建网络的编程人员诅咒,然而,那个应该真正被你鄙视的应该是你自己,由于你没有在第一时间处理这个异常。
让我们看看我们爬虫的第一行,在重要的描述之后,搞明白如何去处理可能的异常:
html = urlopen("http://www.pythonscraping.com/pages/page1.html")
在这一行中,有两个错误的地方:
服务器中没有这个网页(或者获取它有一些错误)
服务器没有发现
在第一种情况下,会返回一个HTTP错误。这个HTTP错误可能是“404 Page Not Found”,“500 Internal Server Error”,等等。在这些情况下,urlopen
会返回一个常见的异常“HTTPError”。我们以下面的方式来处理:
try:
htmul = urlopen("http://www.pythonscraping.com/pages/page1.html")
except HTTPError as e:
#return null, break, or do some other "Plan B"
else:
#program continues. Note: If you return or break in the exception catch, you do not need to use "else" statement.
如果一个HTTP错误代码被返回,程序会输出错误,且不会执行else
下的其他代码。
HTTPError
需要加入包from urllib.error import HTTPError
如果始终没有发现服务器(比如说:http://www.pythonscraping.com 宕机了,或者 URL 被错误录入),urlopen
会返回一个 None
对象。这个对象与其他编程语言的null
一致。我们可以增加一个检测去看看返回的是否是None
:
if html is None:
print("URL is not found")
else:
#program continues
当然,如果网页能够成功地从服务器检索出来,仍然有一些问题,即网页的内容并不是我们所期待的。每一次,你调用BS对象的一个标签,检查该标签是否存在是非常明智的。如果你尝试去调用一个不存在的标签,BS会返回一个None
对象。问题是,尝试调用一个None
对象的标签会返回一个AttributeError
结果。
下面的语句会返回一个None
对象:
print(bsObj.nonExistentTag)
其中,nonExistentTag
是一个捏造的标签,并不是一个真正的BS函数。对这个对象的处理和检查时完全合理的。如果你不检查它,反而继续且试图去调用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
错误,然后,封装了两个BS语句。任何一个AttributeError
都可能从这两个语句中出现(比如:如果服务器不存在,html
会返回一个None
对象,然后html.read()
会抛出一个AttributeError
)。
当写爬虫的时候,很重要的一点就是思考你的代码的全部的模式,这是为了处理异常以及代码具有可读性。你也有可能会再次使用这些代码。构造常用的函数,比如getSiteHTML
和getTitle
,会是得爬整个网络变得快速以及可靠。