Python爬虫实战:使用BeautifulSoup解析HTML文档详解
本文深入讲解Python中BeautifulSoup库的使用,涵盖从安装到高级应用的全流程。内容详解HTML树形结构、Tag与NavigableString数据类型,并通过遍历文档树与搜索文档树(find/find_all)两种核心方法,演示如何高效定位和提取HTML中的目标数据,结合代码示例帮助开发者快速掌握网页数据抓取技巧。
前言
在Python爬虫系列的第3篇文章中,我们介绍了网络请求利器Requests。当成功获取数据后,下一步便是提取目标信息。不同网站返回的数据格式各异,包括对开发者友好的JSON格式、XML格式,以及最常见的HTML文档格式。本文将重点探讨如何从HTML文档中精准提取所需数据。
是自行编写HTML解析器,还是使用正则表达式?这些方法往往并非最优解。幸运的是,Python社区早已提供了成熟的解决方案,其中BeautifulSoup便是处理此类问题的强力工具。它专为HTML文档操作设计,其名称源自Lewis Carroll的同名诗歌。
BeautifulSoup是一个用于解析HTML文档的Python库。借助它,开发者仅需少量代码即可提取HTML中任意感兴趣的内容。此外,该库具备出色的HTML容错能力,即便面对格式不完整的HTML文档,也能进行正确处理。
安装BeautifulSoup
pip install beautifulsoup4
请注意,官方已停止维护BeautifulSoup3,请务必下载并安装最新版本BeautifulSoup4。
HTML标签
在学习BeautifulSoup4之前,有必要先对HTML文档结构有基本了解。如下代码所示,HTML采用树形组织结构。
<html> <head> <title>hello, world</title> </head> <body> <h1>BeautifulSoup</h1> <p>如何使用BeautifulSoup</p> <body> </html>
- HTML由众多标签(Tag)构成,例如html、head、title等均为标签。
- 一对标签构成一个节点,例如 ... 构成了根节点。
- 节点之间存在特定关系,例如h1和p互为邻居,它们属于相邻的兄弟(sibling)节点。
- h1是body的直接子(children)节点,同时也是html的子孙(descendants)节点。
- body是p的父(parent)节点,而html则是p的祖辈(parents)节点。
- 嵌套在标签之间的字符串是该节点下的一个特殊子节点,例如“hello, world”也是一个节点,尽管它没有名称。
使用BeautifulSoup
构建BeautifulSoup对象需要两个参数:第一个参数是待解析的HTML文本字符串,第二个参数指定用于解析HTML的解析器。
解析器的作用是将HTML解析为相关对象,而BeautifulSoup则负责数据的增删改查操作。“html.parser”是Python内置的解析器,而“lxml”则是基于C语言开发的解析器,执行速度更快,但需要额外安装。
通过BeautifulSoup对象,即可定位到HTML中的任何一个标签节点。
from bs4 import BeautifulSoup text = """ <html> <head> <title >hello, world</title> </head> <body> <h1>BeautifulSoup</h1> <p class="bold">如何使用BeautifulSoup</p> <p class="big" id="key1"> 第二个p标签</p> <a href="http://foofish.net" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >python</a> </body> </html> """ soup = BeautifulSoup(text, "html.parser") # title 标签 >>> soup.title <title>hello, world</title> # p 标签 >>> soup.p <p class="bold">\u5982\u4f55\u4f7f\u7528BeautifulSoup</p> # p 标签的内容 >>> soup.p.string u'\u5982\u4f55\u4f7f\u7528BeautifulSoup'
BeautifulSoup将HTML抽象为4类主要数据类型:Tag、NavigableString、BeautifulSoup和Comment。每个标签节点对应一个Tag对象,包裹在Tag中的字符串通常是NavigableString对象,而BeautifulSoup对象则代表整个HTML文档。例如:
>>> type(soup) <class 'bs4.BeautifulSoup'> >>> type(soup.h1) <class 'bs4.element.Tag'> >>> type(soup.p.string) <class 'bs4.element.NavigableString'>
Tag
每个Tag都拥有一个名称,对应HTML的标签名。
>>> soup.h1.name u'h1' >>> soup.p.name u'p'
标签还可以拥有属性,其访问方式类似于字典,返回一个列表对象。
>>> soup.p['class'] [u'bold']
NavigableString
要获取标签中的内容,可直接使用.string属性,这将返回一个NavigableString对象。你可以将其显式转换为Unicode字符串。
>>> soup.p.string u'\u5982\u4f55\u4f7f\u7528BeautifulSoup' >>> type(soup.p.string) <class 'bs4.element.NavigableString'> >>> unicode_str = unicode(soup.p.string) >>> unicode_str u'\u5982\u4f55\u4f7f\u7528BeautifulSoup'
基本概念介绍完毕后,正式进入主题:如何从HTML中查找关心的数据?BeautifulSoup提供了两种主要方式:遍历和搜索,通常需结合使用以完成任务。
遍历文档树
遍历文档树,顾名思义,是从根节点html标签开始遍历,直至找到目标元素。该方法的一个缺陷是,若目标内容位于文档末尾,则需遍历整个文档,导致速度较慢。因此,还需配合第二种方法使用。
通过遍历文档树获取标签节点,可直接通过.标签名的方式获取,例如:
获取body标签:
>>> soup.body <body>\n<h1>BeautifulSoup</h1>\n<p class="bold">\u5982\u4f55\u4f7f\u7528BeautifulSoup</p>\n</body>
获取p标签
>>> soup.body.p <p class="bold">\u5982\u4f55\u4f7f\u7528BeautifulSoup</p>
获取p标签的内容
>>> soup.body.p.string \u5982\u4f55\u4f7f\u7528BeautifulSoup
前文提到,内容也是一个节点,此处可通过.string方式获取。遍历文档树的另一缺点是仅能获取匹配的第一个子节点。例如,若存在两个相邻的p标签,第二个标签无法通过.p方式获取,此时需借助next_sibling属性获取相邻且位于后方的节点。此外,还有许多较少使用的属性,如.contents用于获取所有子节点,.parent用于获取父节点,更多参考请查阅官方文档。
搜索文档树
搜索文档树通过指定标签名来搜索元素,还可通过指定标签的属性值来精确定位节点。最常用的两个方法是find和find_all,这两个方法在BeautifulSoup和Tag对象上均可调用。
find_all()
find_all( name , attrs , recursive , text , **kwargs )
find_all的返回值是由Tag组成的列表,方法调用非常灵活,所有参数均为可选。
第一个参数name为标签节点的名字。
# 找到所有标签名为title的节点
>>> soup.find_all("title")
[<title>hello, world</title>]
>>> soup.find_all("p")
[<p class="bold">\xc8\xe7\xba\xce\xca\xb9\xd3\xc3BeautifulSoup</p>,
<p class="big"> \xb5\xda\xb6\xfe\xb8\xf6p\xb1\xea\xc7\xa9</p>]
第二个参数为标签的class属性值。
# 找到所有class属性为big的p标签
>>> soup.find_all("p", "big")
[<p class="big"> \xb5\xda\xb6\xfe\xb8\xf6p\xb1\xea\xc7\xa9</p>]
等效于
>>> soup.find_all("p", class_="big")
[<p class="big"> \xb5\xda\xb6\xfe\xb8\xf6p\xb1\xea\xc7\xa9</p>]
由于class是Python关键字,因此此处指定为class_。
kwargs为标签的属性名值对,例如:查找href属性值为“http://foofish.net”的标签。
>>> soup.find_all(href="http://foofish.net" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" ) [<a href="http://foofish.net" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >python</a>]
当然,它还支持正则表达式。
>>> import re
>>> soup.find_all(href=re.compile("^http"))
[<a href="http://foofish.net" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >python</a>]
属性值除具体值和正则表达式外,还可以是布尔值(True/False),表示存在或不存在该属性。
>>> soup.find_all(id="key1") [<p class="big" id="key1"> \xb5\xda\xb6\xfe\xb8\xf6p\xb1\xea\xc7\xa9</p>] >>> soup.find_all(id=True) [<p class="big" id="key1"> \xb5\xda\xb6\xfe\xb8\xf6p\xb1\xea\xc7\xa9</p>]
遍历与搜索相结合查找,先定位到body标签以缩小搜索范围,再从body中查找a标签。
>>> body_tag = soup.body
>>> body_tag.find_all("a")
[<a href="http://foofish.net" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >python</a>]
find()
find方法与find_all类似,唯一区别在于它返回单个Tag对象而非列表。若未找到匹配节点则返回None;若匹配多个Tag,仅返回第0个。
>>> body_tag.find("a")
<a href="http://foofish.net" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >python</a>
>>> body_tag.find("p")
<p class="bold">\xc8\xe7\xba\xce\xca\xb9\xd3\xc3BeautifulSoup</p>
get_text()
获取标签内容时,除使用.string外,还可使用get_text方法。不同之处在于,前者返回NavigableString对象,后者返回Unicode类型的字符串。
>>> p1 = body_tag.find('p').get_text()
>>> type(p1)
<type 'unicode'>
>>> p1
u'\xc8\xe7\xba\xce\xca\xb9\xd3\xc3BeautifulSoup'
>>> p2 = body_tag.find("p").string
>>> type(p2)
<class 'bs4.element.NavigableString'>
>>> p2
u'\xc8\xe7\xba\xce\xca\xb9\xd3\xc3BeautifulSoup'
>>>
在实际场景中,我们通常使用get_text方法获取标签内容。
总结
BeautifulSoup是一个用于操作HTML文档的Python库。初始化BeautifulSoup时,需指定HTML文档字符串及具体解析器。BeautifulSoup包含3类常用数据类型:Tag、NavigableString和BeautifulSoup。查找HTML元素有两种方式:遍历文档树和搜索文档树,通常快速获取数据需二者结合。
以上即为本文的全部内容,希望本文内容对大家学习或使用Python有所帮助。如有任何疑问,欢迎留言交流,感谢大家对本站的支持。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









