BeautifulSoup
1. BeautifulSoup 简介
BeautifulSoup 尝试化平淡为神奇。它通过定位 HTML 标签来格式化和组织复杂的网页信息,用简单易用的 Python 对象为我们展现 XML 结构信息。
2. 安装 BeautifulSoup
由于 BeautifulSoup 库不是 Python 标准库,因此需要单独安装。如果你安装过 Python 库,可以使用你最喜爱的安装器并略过本小节;
我们将使用 BeautifulSoup 4(也叫 BS4 )。
Linux 系统上的基本安装方法是:
sudo apt-get install python-bs4
对于 macOS 系统,首先用以下命令安装 Python 的包管理器 pip:
sudo easy_install pip
然后运行以下命令安装库:
pip install beautifulsoup4
windows 下可以直接运行以下命令:
pip install beautifulsoup4
这样就可以了,BeautifulSoup 将被当作设备上的一个 Python 库。你可以在 Python 终端里导入它测试一下:
如果没有错误,说明导入成功了。
3. 运行 BeautifulSoup
BeautifulSoup 库最常用的对象恰好就是 BeautifulSoup 对象。让我们运行以下代码看看:
from urllib.request import urlopen
from bs4 import BeautifulSoup
html = urlopen('http://www.pythonscraping.com/pages/page1.html')
bs = BeautifulSoup(html.read(),'html.parser')print(bs.h1)
输出结果是:
<h1>Totally Normal Gifts</h1>
这里仅仅返回了页面上的第一个 h1 标签实例。通常情况下,一个页面也只有一个 h1 标签,但是在 Web 中这个惯例经常被打破,因此你应该意识到这里仅仅检索了该标签的第一个实例,而不一定是你寻找的那个。
和前面网页抓取的例子一样,你导入 urlopen 函数,然后调用 html.read() 获取网页的 HTML 内容。除了文本字符串,BeautifulSoup 还可以使用 urlopen 直接返回的文件对象,而不需要先调用 .read() 函数:
bs = BeautifulSoup(html,'html.parser')
这样你就可以把 HTML 内容传递到 BeautifulSoup 对象,转换成下面的结构:
- html → < html>< head>…< /head>< body>…< /body>< html> — head→ < head>< title>A Useful Page< /title>< /head> —— title→ < title>A Useful Page< /title> — body→ < body>< h1>An Int…< /h1>< div>Lorem ip…< /div>< /body> —— h1→ < h1>An Interesting Title< /h1> —— div→ < div>Lorem Ipsum dolor…< /div> 可以看出,我们从网页提取的 < h1> 标签被嵌在 BeautifulSoup 对象结构的第二层(html → body → h1)。但是,当我们从对象里提取 h1 标签的时候,可以直接调用它:
bs.h1
其实,下面的所有函数调用都可以产生相同的结果:
bs.html.body.h1
bs.body.h1
bs.html.h1
当你创建一个 BeautifulSoup 对象时,需要传入两个参数:
bs = BeautifulSoup(html.read(),'html.parser')
第一个参数是该对象所基于的 HTML 文本,第二个参数指定了你希望 BeautifulSoup 用来创建该对象的解释器。在大多数情况下,你选择任何一个解释器都差别不大(html.parser 是 Python 3 中的一个解释器,不需要单独安装)。
4.可靠的网络连接以及异常的处理
Web 是十分复杂的。网页数据格式不友好、网站服务器死机、目标数据的标签找不到了,都是很麻烦的事情。网页抓取最痛苦的遭遇之一,就是爬虫运行的时候你该洗洗睡了,想着第二天一早数据就都会抓取好放在数据库里,结果第二天醒来,你看到的却是一个因某种数据格式异常导致运行错误的爬虫。那个时候,你可能会骂发明网站(以及那些奇葩的网络数据格式)的人,但你真正应该训斥的人是你自己,为什么不在一开始就估计可能发生的异常!
让我们看看爬虫 import 语句后的第一行代码,看看如何处理可能出现的异常:
html = urlopen('http://www.pythonscraping.com/pages/page1.html')
这行代码主要会发生两种异常:
- 网页在服务器上不存在(或者获取页面的时候出现错误)
- 服务器不存在
发生第一种异常时,程序会返回 HTTP 错误。HTTP 错误可能是“404 Page Not Found” “500 Internal Server Error” 等。对于所有类似情形,urlopen 函数都会抛出 HTTPError 异常。我们可以用下面的方式处理这种异常:
from urllib.request import urlopen
from urllib.error import 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 会抛出一个 URLError 异常。你可以增加以下检查代码:
from urllib.request import urlopen
from urllib.error import HTTPError
from urllib.error import URLError
try:
html = urlopen('http://www.pythonscraping.com/pages/page1.html')except HTTPError as e:print(e)except URLError as e:print('The server could not be found')else:print('It worked!')
当然,即使从服务器成功获取网页,如果网页上的内容并非完全是我们所期望的那样,仍然可能会出现异常。每当你调用 BeautifulSoup 对象里的一个标签时,增加一个检查条件以保证标签确实存在是很聪明的做法。如果你想要调用的标签不存在,BeautifulSoup 就会返回 None 对象。不过,如果再调用这个 None 对象下面的子标签,就会发生 AttributeError 错误。
下面这行代码(nonExistentTag 是虚拟的标签,BeautifulSoup 对象里其实没有)
print(bs.nonExistentTag)
会返回一个 None 对象。处理和检查这个对象是十分必要的。如果你不检查,直接调用这个 None 对象的子标签,就会有麻烦,如下所示:
print(bs.nonExistentTag.someTag)
这时就会返回一个异常:
AttributeError: 'NoneType' object has no attribute 'someTag'
那么怎么才能避免这两种情形的异常呢?最简单的方式就是对这两种情形进行检查:
try:
badContent = bs.nonExistentTag.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
defgetTitle(url):try:
html = urlopen(url)except HTTPError as e:returnNonetry:
bs = BeautifulSoup(html.read(),'html.parser')
title = bs.body.h1
except AttributeError as e:returnNonereturn title
title = getTitle('http://www.pythonscraping.com/pages/page1.html')if title ==None:print('Title could not bu found')else:print(title)
在这个例子中,我们创建了一个 getTitle 函数,它可以返回网页的标题,如果获取网页的时候遇到问题就返回一个 None 对象。在 getTitle 函数里面,我们像前面那样检查了 HTTPError,还检查了由于 URL 输入错误引起的 URLError,然后把两行 BeautifulSoup 代码封装在一个 try 语句里面。这两行中的一行有问题,都可能抛出 AttributeError(如果服务器不存在,html 就是一个 None 对象,html.read() 就会抛出AttributeError)。
在写爬虫的时候,思考代码的总体格局,让代码既可以捕捉异常又容易阅读,这是很重要的。如果你还希望重用大量代码,那么拥有像 getSiteHTML 和 getTitle 这样的通用函数(具有周密的异常处理功能)会让快速、稳定地抓取网页变得简单易行。
版权归原作者 QuantumStack 所有, 如有侵权,请联系我们删除。