Python 网络爬虫从0到1 (4):Beautiful Soup 4库入门详解

  从先前的几篇文章中,我们已经能够使用Requests库构造请求并获得正确的响应,但是在样例中我们也发现了仅使用Requests库的缺陷。在网络爬虫的设计中,仅能构造请求并收到响应是远远不够的。想要获取响应中有意义的信息并能够由此采取下一步行动,是整个流程中的重要一段。所以本篇文章,我们就来一起学习较为流行的HTML/XML数据分析提取库:Beautiful Soup 4

Beautiful Soup 4

Beautiful Soup 4介绍

  Beautiful Soup 4库是一个在Python爬虫设计中非常流行的数据分析提取库。它能够解析HTML/XML语言并生成文档树。由于其简单易用的特性被许多小型爬虫所采用。

  HTML语言作为一种标记语言,每一个元素都由一对标签包含,层层嵌套形成树状结构。当人去阅读这段代码时,实际内容会被一大串描述元素样式的代码淹没,观察较为繁琐;但程序阅读恰恰相反,标签化、结构化且严谨的语法非常适合程序(浏览器等)理解分析,并按照当初设计的方式渲染重构网页。

  我们获得的来自服务器的响应一般为HTML格式的一个页面,由于HTML语言特性,人类难以直观理解实际效果,这就需要一个程序来解析这个响应。浏览器核心也包含一个HTML解析器,而在爬虫中,Beautiful Soup 4库就起到了这样的效果。

  至于这个Python库的名称为什么是BeautifulSoup4(即bs4)而不是BeautifulSoup,是因为后者已经用来表示BeautifulSoup3库(为了兼容性原因仍然保留),为避免名称冲突,才使用这个名字。

  关于这个库的更多信息以及使用指南,请参阅Beautiful Soup 4官方手册(中文)Beautiful Soup 4.4.0 文档


Beautiful Soup 4库的安装、引用与测试

  • Beautiful Soup 4包安装与安装其他库包的方法相同,Windows cmd/Linux Terminal 下,输入如下命令
pip install beautifulsoup4

等待安装完成即可。

  • 引用这个库的方法与其他库略有不同,实际上,Beautiful Soup 4是一个类,包含在bs4包中,使用时只需要从包中单独引用BeautifulSoup类即可。使用from-import语句实现
from bs4 import BeautifulSoup

注意:BeautifulSoup中的BS为大写字母,Python对大小写敏感

  • 下面使用一个小样例来测试Beautiful Soup 4的功能,该类的基本功能会在下面讲到

示例实现步骤

  1. 使用Requests库下载了一个样例页面
  2. 将响应主体(HTML格式)作为参数声明了一个BeautifulSoup实例soup
  3. 使用其prettify()方法将主体内容进行了优化并输出
import requests
from bs4 import BeautifulSoup

r = requests.get("http://python123.io/ws/demo.html")
# 提交请求抓取响应
# print(r.text)
soup = BeautifulSoup(r.text, features="html.parser")
# 声明实例并将响应主体作为参数传入
print(soup.prettify())
#使用prettify()方法输出优化后的内容


BeautifulSoup类使用

 HTML基础知识

在了解BeautifulSoup类的原理前,先来补充一点HTML语言的相关知识。

HTML语言的各个元素均由标签构成,一对一对标签以及中间包括的内容构成了元素,例如:

<p class="title">Hello World</p>

其中,

  • p:标签名称,p表示段落(paragraph)

  • class="title":标签的属性,属性键为class,值为"title",这个属性与CSS样式表相关

  • Hello World:标签主体内容

  • /p:标签结束符号,与p标签开始符号一一对应

  • 标签开始部分(标签名称和属性)与标签结束都由尖括号包含,两个尖括号中间的内容为标签主体内容

这样的标签嵌套叠加,形成了一个树状结构,被称为文档树

HTML文档树

附表:HTML常用标签

标签 名称 说明
<a> 链接
<b> 普通文本
<body> 文档主体内容标签
<div> HTML文档中的分隔区块
<form> 创建表单用于用户输入
<hn> 标题,n为等级1-6
<img> 图片
<span> 对一部分文本进行着色
<script> JavaScript脚本

 BeautifulSoup类实例化

BeautifulSoup类中的一个实例正与一个文档树一一对应(类本身也是)。**声明一个新的实例时,将HTML内容作为参数传递给实例初始化函数,这个HTML内容所对应的文档树就按照BeautifulSoup类的结构被对应到了实例中。**通过Beautiful Soup库,我们能够对文档树进行解析、遍历、维护。

在初始化一个BeautifulSoup实例时,不仅需要提供HTML文档内容作为参数,还需要选择一个适合的解释器

解析器 名称 优点 缺点
Python标准库解析器 html.parser 是Python内置标准库 执行速度适中 文档容错能力强 较早版本Python中的解析器容错能力弱且不稳定
lxml HTML解析器 lxml 速度快 文档容错能力强 依赖C语言库
lxml XML解析器 lxml-xml 速度快 唯一一个XML解析器 依赖C语言库
html5lib html5lib 容错性最佳 以浏览器方式解析文档 生成html5格式文档 速度慢 不依赖外部拓展

其中,Python标准库解析器在Python标准库中,不需要另外安装;其他的解析器需要自行安装第三方库(lxml的两个解析器安装lxml包,html5lib解析器安装html5lib包)

声明一个新实例的语法(尖括号仅表示名称,不在语法中)

<Name> = BeautifulSoup(<HTML Document>, features="<Parser Name>")

其中,

  • Name:新实例的名称
  • HTML Document:需要解析的HTML文档,以字符串形式,也可以为打开的文件
  • Parser Name:解析器名称,各个解析器的名称见上表

 BeautifulSoup类的基本元素

基本元素 说明
Tag 标签,为文档的最基本组成元素,上面已经介绍
Name 标签的名称,例如

标签名称为p,调用格式为<tag>.name
Attributes 标签的属性,以字典形式组织,调用格式为<tag>.attrs
NavigableString 可以遍历的字符串,为标签内非属性字符串,即为上面提到的标签主体内容,格式<tag>.string
Comment 标签内字符串的注释部分,是一种特殊的NavigableString,格式同上
Text 标签中的主体内容(不含注释),格式<tag>.text
Beautiful Soup 整个文档对应的类的实例,这个元素的名称为[document]
  • 标签元素的类型为bs4.element.Tag
  • 标签的NavigableString不是一个str类型数据,而是Beautiful Soup 4中另行定义的类型bs4.element.NavigableString
  • 关于各种属性的使用方法,请见下面的BeautifulSoup标签的属性

 BeautifulSoup类获取标签

基本元素中,除了Tag为独立的标签元素外,其余所有都可以视作Tag的属性,那么,如何从BeautifulSoup类中提取一个标签呢?我们以实例soup为例

tag_a = soup.a
tag_p = soup.p

实际上,使用soup.就可以获得实例对应文档树中第一个名称为tag name的标签,如果文档树中不存在这个标签,不会报错,而是返回空,且这些标签名称由于不固定,IDE不提供代码补全,所以要小心标签名称错误

注意,使用这个方法只能获得文档中的一个标签,如果需要其他标签,请使用后面讲到的文档遍历

 BeautifulSoup标签的属性

标签的属性在上面的基本元素中已经基本给出,下面挑选必要的属性逐个介绍

  • Attributes:标签属性域中的属性,为字典形式,对于HTML中已经定义的多值属性,将返回一个List(即使只有一个也返回List)
    • 如果该标签没有属性,将返回为空
    • 由于属性为字典格式,可以使用字典的相关操作,例如单独输出某个键的值tag.attrs['class']
  • NavigableString:标签中的主要内容(可遍历字符串),即两对尖括号中间的内容
    • 如果该标签没有可遍历字符串,则返回空
    • 如果不为空,NavigableString不是一个str普通字符串型数据,而是Beautiful Soup 4中另行定义的类型bs4.element.NavigableString
    • 如果一个标签的主体部分既有文本又有注释,则string为空,请见Text
  • Comment:标签中的注释内容,注意:Comment为一种特殊的NavigableString,其使用方法与返回形式完全相同,即使用string,两个返回格式相同,无法从这里区分CommentNavigableString,需要用类型或Text判断
    • 不同于普通的NavigableStringComment有自己独有的类型bs4.element.Comment
    • 如果一个标签的主体部分既有文本又有注释,则string为空,请见Text
  • Text:标签中的主要内容(不包含注释),也是两对尖括号中的内容
    • 与NavigableString不同的是,Text在文本与注释同时出现时,会过滤注释内容,仅保留文本内容
    • Text的类型为str,为Python标准类型

 Beautiful Soup HTML文档遍历方法

Beautiful Soup 支持HTML文档树的上行遍历下行遍历平行遍历三种遍历方法。所有遍历均通过查询当前标签的属性实现,即

<tag>.<attribute>

 下行遍历

从根节点元素开始向文档叶节点遍历(寻找子节点)

Beautiful Soup 为下行遍历提供了三个属性

属性 说明
.contents 子节点列表,将这个标签的所有子节点(下一层)以列表形式给出
.children 子节点的可迭代类型对象,生成这个标签所有子节点(下一层)的可迭代列表对象
.descendants 子孙节点的可迭代类型对象,包含这个标签所有子孙节点(下面所有)
  • 注意:子节点不仅有标签类型,也有其他字符串,如\n换行符或纯文本,这些字符串的类型为NavigableString
  • 可迭代类型的原文为list_iterator object
  • .descendants的遍历顺序为先序遍历

 上行遍历

从叶节点元素开始向文档根节点遍历(寻找父节点)

Beautiful Soup 为上行遍历提供了两个属性

属性 说明
.parent 父节点,这个标签的父节点,仅一个
.parents 先辈节点、这个节点的所有父节点的迭代(父节点、父节点的父节点……)
  • 仅在最高一级的根节点[document]节点(参见上文HTML基础知识中文档树结构图)的类型为BeautifulSoup,即为BeautifulSoup本身,其他节点的类型均为bs4.element.Tag
  • 根节点的父节点为空

 平行遍历

在平行节点间横向遍历,平行节点,即兄弟节点,指父节点为同一个节点的一系列节点

Beautiful Soup 为平行遍历提供了四个属性

属性 说明
.next_sibling 按照HTML文本序的下一个平行节点
.previous_sibling 按照HTML文本序的上一个平行节点
.next_siblings 按照HTML文本序的下面所有平行节点
.previous_sibling 按照HTML文本序的上面所有平行节点
  • 注意:平行节点不仅有标签类型,也有其他字符串,如\n换行符或纯文本等,这些字符串的类型为NavigableString,使用时请注意区分

  • 可以通过判断该元素的name属性是否为None来确定该元素是否为NavigableString(若使用类型判断,则需要导入整个bs4)

    if not tag.name is None:
        do_something()
    

 Beautiful Soup HTML文档优化

使用Requests库得到的响应文档未经过处理(换行、缩进等),且由于HTML的特性,没有渲染情况下可读性较差,需要通过优化提升可读性,Beautiful Soup类的Prettify()方法提供了这个功能。需要将返回值打印才能看到效果(只是在其中添加换行符)

soup = BeautifulSoup(HTML_text, "html.parser")
print(soup.prettify())

 基于Beautiful Soup库的HTML内容查找方法

  查找标签find_all()

Beautiful Soup提供了一个专用的方法用来查找指定名称的标签

soup.find_all('name')
#soup 为搜索范围的Tag或BeautifulSoup对象,name 为要查找的标签名称

函数返回一个列表,包含所有符合名称的标签

  • 由于列表可遍历(可迭代),可以使用for语句遍历整个搜索结果列表,列表中的元素类型仍然为bs4.element.Tag

  • 当想要获取多个类型的标签时,可以将一个包含有欲查找标签名称(字符串类型)的列表作为参数传入标签

  • 当想要获得这个soup中的所有标签时(相当于树遍历),可以将参数变为True

  • 如果还想要进一步限制查找内容,find_all()函数还提供了class属性参数

    soup.find_all('name', 'attr')
    

    这将搜索所有名称为nameclass类属性为attr的标签
    如果将name设为空'',则将返回所有class符合的标签,而不限制标签名称

  • 还可以限制id属性

    soup.find_all(id = 'link1')
    

    这将搜索所有id属性为"link1"的标签

  • 这个函数的另一个参数recursive递归,默认为True,表示将遍历该标签对象下的所有子孙节点,否则只遍历该节点的子节点

  • 又一个参数string,可以搜索标签主体内容(一对尖括号中间的内容),返回类型为NagivableString

    必须精确匹配内容才能搜索到,实际没有太大意义,以后将使用正则表达式标准库re来完成正则表达式匹配搜索

 其他查找语句

除了find_all()外,Beautiful Soup还提供了其他特性的搜索函数,这些函数的参数与find_all()相同,有对应需求时,直接使用函数即可

函数 说明
.find() 搜索且只返回第一个结果
.find_parents() 在先辈节点中搜索,返回列表
.find_parent() 在先辈节点中搜索,且只返回一个结果
.find_next_siblings() 在后续平行节点中搜索,返回一个列表
.find_next_sibling() 在后续平行节点中搜索,且只返回一个结果
.find_previous_siblings() 在之前的平行节点中搜索,返回一个列表
.find_previous_sibling() 在之前的平行节点中搜索,且只返回一个结果

猜你喜欢

转载自blog.csdn.net/Zheng__Huang/article/details/108653153