“美味的汤,绿色的浓汤, 在热气腾腾的盖碗里装! 谁不愿意尝一尝,这样的好汤? 晚餐用的汤,美味的汤!” BeautifulSoup 库的名字取自刘易斯 •卡罗尔在《爱丽丝梦游仙境》里的同名诗歌。在故事中,这首诗是素甲鱼1 唱的。 就像它在仙境中的说法一样,BeautifulSoup 尝试化平淡为神奇。它通过定位HTML 标签来格式化和组织复杂的网络信息,用简单易用的Python 对象为我们展现XML 结构信息。 注1: Mock Turtle,它本身是一个双关语,指英国维多利亚时代的流行菜肴素甲鱼汤,其实不是甲鱼而是牛肉, 如同中国的豆制品素鸡,名为素鸡,其实与鸡无关。 1.2.1 安装BeautifulSoup 由于BeautifulSoup 库不是Python 标准库,因此需要单独安装。在本书中,我们将使用最新的BeautifulSoup 4 版本(也叫BS4)。BeautifulSoup 4 的所有安装方法都在http://www. crummy.com/software/BeautifulSoup/bs4/doc/ 里面。Linux 系统上的基本安装方法是: $sudo apt-get install python-bs4 对于Mac 系统,首先用 $sudo easy_install pip 安装Python 的包管理器pip,然后运行 $pip install beautifulsoup4 来安装库文件。 另外,注意如果你的设备同时安装了Python 2.x 和Python 3.x,你需要用python3 运行Python 3.x: $python3 myScript.py 当你安装包的时候,如果有可能安装到了Python 2.x 而不是Python 3.x 里,就需要使用: $sudo python3 setup.py install 如果用pip 安装,你还可以用pip3 安装Python 3.x 版本的包: $pip3 install beautifulsoup4 在Windows 系统上安装与在Mac 和Linux 上安装差不多。从上面的下载链接下载最新的BeautifulSoup 4 源代码,解压后进入文件,然后执行: >python setup.py install 这样就可以了! BeautifulSoup 将被当作设备上的一个Python 库。你可以在Python 终端里导入它测试一下: $python > from bs4 import BeautifulSoup 如果没有错误,说明导入成功了。 另外,还有一个Windows 版pip(https://pypi.python.org/pypi/setuptools)的.exe 格式安装器 ,装了之后你就可以轻松安装和管理包了: >pip install beautifulsoup4 用虚拟环境保存库文件 如果你同时负责多个Python 项目,或者想要轻松打包某个项目及其关联的库文件,再或者你担心已安装的库之间可能有冲突,那么你可以安装一个Python 虚拟环境来分而治之。 当一个Python 库不用虚拟环境安装的时候,你实际上是全局安装它。这通常需要有管理员权限,或者以root 身份安装,这个库文件对设备上的每个用户和每个项目都是存在的。好在创建虚拟环境非常简单: $ virtualenv scrapingEnv 这样就创建了一个叫作scrapingEnv 的新环境,你需要先激活它再使用: $ cd scrapingEnv/ $ source bin/activate 激活环境之后,你会发现环境名称出现在命令行提示符前面,提醒你当前处于虚拟环境中。后面你安装的任何库和执行的任何程序都是在这个环境下运行。 在新建的scrapingEnv 环境里,可以安装并使用BeautifulSoup: (scrapingEnv)ryan$ pip install beautifulsoup4 (scrapingEnv)ryan$ python > from bs4 import BeautifulSoup > 当不再使用虚拟环境中的库时,可以通过释放命令来退出环境: (scrapingEnv)ryan$ deactivate ryan$ python > from bs4 import BeautifulSoup Traceback (most recent call last): File "<stdin>", line 1, in <module> ImportError: No module named 'bs4' 将项目关联的所有库单独放在一个虚拟环境里,还可以轻松打包整个环境发生给其他人。只要他们的Python 版本和你的相同,你打包的代码就可以直接通过虚拟环境运行,不需要再安装任何库。 尽管本书的例子都不要求你使用虚拟环境,但是请记住,你可以在任何时候激活并使用它。 1.2.2 运行BeautifulSoup BeautifulSoup 库最常用的对象恰好就是BeautifulSoup 对象。让我们把本章开头的例子调整一下运行看看: from urllib.request import urlopen from bs4 import BeautifulSoup html = urlopen("http://www.pythonscraping.com/pages/page1.html") bsObj = BeautifulSoup(html.read()) print(bsObj.h1) 输出结果是: <h1>An Interesting Title</h1> 和前面例子一样,我们导入urlopen,然后调用html.read() 获取网页的HTML 内容。这样就可以把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 对象bsObj 结构的第二层(html → body → h1)。但是,当我们从对象里提取h1 标签的时候,可以直接调用它: bsObj.h1 其实,下面的所有函数调用都可以产生同样的结果: bsObj.html.body.h1 bsObj.body.h1 bsObj.html.h1 希望这个例子可以向你展示BeautifulSoup 库的强大与简单。其实,任何HTML(或XML)文件的任意节点信息都可以被提取出来,只要目标信息的旁边或附近有标记就行。在第3 章,我们将进一步探讨一些更复杂的BeautifulSoup 函数,还会介绍正则表达式,以及如何把正则表达式用于BeautifulSoup 以对网站信息进行提取。 1.2.3 可靠的网络连接 网络是十分复杂的。网页数据格式不友好,网站服务器宕机,目标数据的标签找不到,都是很麻烦的事情。网络数据采集最痛苦的遭遇之一,就是爬虫运行的时候你洗洗睡了,梦想着明天一早数据就都会采集好放在数据库里,结果第二天醒来,你看到的却是一个因某种数据格式异常导致运行错误的爬虫,在前一天当你不再盯着屏幕去睡觉之后,没过一会儿爬虫就不再运行了。那个时候,你可能想骂发明互联网(以及那些奇葩的网络数据格式)的人,但是你真正应该斥责的人是你自己,为什么一开始不估计可能会出现的异常! 让我们看看爬虫import 语句后面的第一行代码,如何处理那里可能出现的异常: 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 就会返回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 的函数。 在写爬虫的时候,思考代码的总体格局,让代码既可以捕捉异常又容易阅读,这是很重要的。如果你还希望能够很大程度地重用代码,那么拥有像getSiteHTML 和getTitle 这样的通用函数(具有周密的异常处理功能)会让快速稳定地网络数据采集变得简单易行。
Python网络数据采集——1.2 BeautifulSoup简介
书名: Python网络数据采集
作者: [美] 米切尔
出版社: 人民邮电出版社
译者: 陶俊杰 | 陈小莉
出版年: 2016-3-1
页数: 200
定价: CNY 59.00
装帧: 平装
ISBN: 9787115416292