Beautifulsoup4入门使用

April 12, 2020

4042 18 minutes and 22 seconds
Beautifulsoup4入门使用

[DeadPool爬虫]Beautifulsoup4入门使用

上一篇博文介绍了selenium的入门级用法,虽说是入门级用法,通过代码多加练习其实就已经算是掌握了其精髓,之后的具体问题只需要通过平时的不断编码练习中,慢慢积攒学习就行了。接下来我们还需要掌握一个爬虫里面的必备库–Beautifulsoup4,通过这个库,将使我们通过requests请求的页面解析变得简单无比,再也不用通过绞尽脑汁的去想如何正则该如何匹配内容了。(PS: 一入正则深似海)

再次重申,博文中介绍的内容只是个人的理解,想要掌握完全正确的知识内容还是看官方文档–Beautifulsoup4官方文档

PS: 如果您已经有爬虫相关知识了,可以直接看我的框架地址Deadpool项目

Beautifulsoup4 介绍

Beautifulsoup4 是 Beautiful Soup 项目的第四个版本,也是当前的最新版本。官方文档中明确的介绍了Beautifulsoup4究竟是一个什么样的库,以及它能解决什么样的问题。引用一下官方的介绍:

Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库.它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式.Beautiful Soup会帮你节省数小时甚至数天的工作时间.

OK,通过官方的介绍说明,明确了一个概念,就是这个库具体是做什么,接下来正常情况是要介绍这个库的使用方法,但是我觉得先按下不表,还是先补充一下其他一些基本概念~

数据结构–树

这里只是简单回顾一下数据结构中关于树的基本知识,有个概念就行,也是帮助后面深入理解这个Beautifulsoup库的使用,这点非常重要,想要掌握明白这个概念还是要有的。

形态

树结构的样子如下,在脑海中明确其长相和内容

graph TB A --> B A --> C A --> D B --> E B --> F C --> G D --> H D --> I D --> J E --> K H --> M
结点

结点:上面的示意图中每一个数据元素都被称为"结点"。

结点的度:结点所拥有的子树的个数称为该结点的度。 上图中A节点的子树的数量就是三个,它的度就是3。

根结点:每一个非空树都有且只有一个被称为根的结点。 上图中里面的A就是当前树的根节点。

子结点、父结点、兄弟结点:树中一个结点的子树的根结点称为这个结点的子结点,这个结点称为孩子结点的父结点。具有同一个父结点的子结点互称为兄弟结点。 上图中B、C、D就是兄弟节点,同时也是A的孩子节点,C是G双亲节点

叶子结点:度为0的结点称为叶子结点,或者称为终端结点。 上图中的K、M就是叶子节点的代表

HTML解析中DOM树

HTML文档在解析过程中也是通过树的结构进行的,其中,解析的结果就是我们经常说的DOM树,这里举个例子直观的展示一下其效果。


<!DOCTYPE html>
<html>
	<head>
	    <meta charset="UTF-8">
	    <link rel="stylesheet" type="text/css" href="style.css">
	    <script type="application/javascript" src="script.js"></script>
	    <title>I’m the title</title>
	</head>
	<body>
	    <h1>HelloWorld</h1>
	    <div>
	        <div>
	            <p>picture:</p>
	            <img src="example.png"/>
	        </div>
	        <div>
	            <p>A paragraph of explanatory text...</p>
	        </div> 
	    </div>
	</body>
</html>

上面的HTML源码通过HTML文档解析构建DOM树会形成如下的效果。

graph TB html --> head html --> body head --> meta head --> link head --> script head --> title body --> h1 body --> A[div] A[div] --> C[div] A[div] --> D[div] C[div] --> E[P] C[div] --> img D[div] --> F[P]

可以看得出来一个html文档在执行页面解析的过程中是按照DOM树的结构进行解析的,这里同样是有这么一个概念就行,具体还有一些其他的很多内容值得深入学习的,如果有机会也会通过博文介绍一下,当前自己才疏学浅,掌握不多~

BeautifulSoup解析结构

既然BeautifulSoup4是用来解析HTML的页面,那么其解析的过程是否跟DOM树的结构一致呢?虽然这里并没有找到示例作为参考,但是根据官方文档的描述,我们可以将前面一小节内容中的HTML代码绘制成如下效果。(PS:这里只是个人在学习的时候进行对比学习的方式,如果有错误,还请见谅)

graph TB BeautifulSoup --> html html --> head html --> body head --> meta head --> link head --> script head --> title title --> G[I'm a title] body --> h1 h1 --> H[HelloWorld] body --> A[div] A[div] --> C[div] A[div] --> D[div] C[div] --> E[p] E[p] --> I[picture:] C[div] --> img D[div] --> F[p] F[p] --> J[A paragraph of explanatory text...]

对比一下,两种解析的效果,我们可以看出来,在BeatifulSoup4中,其解析过程中,会将标签中的文本内容也当做结点进行存储。

=========

至此,我想阐明的内容就如上面展示的效果一样,一定要注意的就是BeautifulSoup4在解析HTML页面的时候会解析标签内容的~所以这里我总结了一句话作为方便记忆的口诀:在BeautifulSoup4解析的页面结构中,标签内的文本内容一定是叶子结点,虽然总结的不一定对,但是通过这句话希望的是阐明两个问题:

  • BeautifulSoup4解析出来的页面树状结构与普遍认知的DOM树解析结构不同
  • 重点是标签内的文本内容在BeautifulSoup4中会作为结点对待
BeautifulSoup4 使用总结

通过上面的介绍,如果能理解我想表达的意思之后,个人觉得就很容易避免了官方文档中的一些坑。接下来就是个人在研读 BeautifulSoup4 库的时候一些总结,也算是作为代表,展示一下我个人在学习代码的时候,是采用什么方式进行学习的。

# 基本使用

from bs4 import BeautifulSoup
soup = BeautifulSoup(html_doc, 'html.parser')

# 这样就形成了一个 soup 对象,即 BeautifulSoup 解析对象

对象归纳

这里使用类比的方式,十分容易记忆。 将BeautifulSoup4中的对象,类比HTML页面中的元素,就很容易记忆了~

  • BeautifulSoup: 即HTML中的document
  • Tag: 即HTML中的标签元素
  • NavigableString:即HTML标签中的文本内容
  • Comment:一个完整的HTML页面,去除上面的内容,再抛开样式和js脚本,剩下的只能是注释了,所以这里对应HTML中的注释内容

可以看出来,这四大类BeautifulSoup4中的对象,正好将一个完整的HTML页面元素对应起来了,结合HTML的基本知识,也能够方便记忆

BeautifulSoup的一些基本知识

这里大部分时候可以将BeautifulSoup与tag等同,但是需要注意一些具体的内容:


# soup对象中的name中包含了一个值为列表的字符串
soup.name

> u'[document]'

# 获取soup对象的父节点是空,所以通过这里我们可以了解到:
# 1、BeautifulSoup解析的文档树结构中BeautifulSoup对象是根节点
# 2、HTML文档的html标签是BeautifulSoup的子节点
soup.parent

> None

Tag的一些基本知识


# soup 即文档的对象
tag = soup.div # 即通过html标签的名称,获取soup解析对象中的tag对象

# 同时 tag 也提供了一个有趣的方法,个人觉得这个方法是在代码比较庞大的时候
# 因为获取很多对象统一取名为tag的时候,需要明确当前的tag是哪一个时使用。
tag.name

> 'div'

# 获取 tag 的全部标签属性
tag.attrs

# 同样也可以直接获取一个
tag['class']

> ['contents', 'content-wrapper']  # 多值的时候返回列表

NavigableString的一些基本知识


# 获取tag中的字符串
tag.string

# 转化成unicode字符串
unicode_string = unicode(tag.string)

这里官方文档是这么说明的一个 NavigableString 字符串与Python中的Unicode字符串相同,并且还支持包含在 遍历文档树 和 搜索文档树 中的一些特性.其实简单理解成Python中的字符串对象就行了,代码操作中直接将其转化成Unicode字符串就是最理想的选择。

遍历文档树

这里对具体如何通过BeautifulSoup去定位和操作HTML文档中的元素就不多加赘述了,个人建议还是通过代码去练习,自然就掌握了,不过,这里我依然通过类比的方式阐述一下如何比较方便记忆这个块的知识点~

查找父子结点

tag.parent和tag.children 两个方法是查找当前的tag对象的直接父节点或者子节点的方法。可以结合之前的图去理解,即查询的只是与当前结点直接相连的结点。

tag.parents和tag.descendants 这里就有点不同了,查找当前结点的所有父节点即用复数形式就行了,但是查找所有的子孙结点,就出现了专门的一个方法名词,这里很容易理解,因为子节点有可能已经是分支形态了,所以方法定义上就用的children,因此需要一个新的名词。

这里需要注意的是其返回的两个对象都是可以进行递归循环取值的~

查找兄弟结点

tag.next_sibling和tag.previous_sibling 这个就很好理解了,就是前一个和后一个兄弟节点,单数方法名,只返回一个对象

tag.next_siblings和tag.previous_siblings 同样结合上面说的,也很好理解了,复数形态,表明返回的的是一组对象,既然是一组也是同样可以通过递归循环取值的~

查找解析对象

首先我们直接看一下,方法的使用,然后我也同时描述一下其内容中容易忽视的一个小知识点~

tag.next_element和tag.previous_element 通过这两个方法,将当前的tag在解析过程中,所指向的前一个被解析的对象和后一个被解析的对象,单数方法名,返回值也为单一对象

tag.next_elements和tag.previous_elements 通过这两个方法,将当前的tag在解析过程中,所之前解析过的全部对象和之后解析的全部对象,复数方法名,返回值为一组对象,也是可以进行迭代的~

PS: 这里需要结合前文所说的重点内容,即在BeautifulSoup中标签内的文本内容也是一个结点,因此有些情况下查询兄弟节点tag.next_sibling和tag.previous_sibling的方法和查询解析对象tag.next_element和tag.previous_element的方法返回的结果值是一样的,但是当tag出现了文本内容时,就不一定了~

查询对象

find()和find_all() 这两个方法是不是很眼熟,这里类比一下selenium中学到的定位元素的方法看一下~


# Selenium中的方法
driver.find_element(By.XPATH, '//button[text()="Some text"]')
driver.find_elements(By.XPATH, '//button')

# BeautifulSoup中的方法
soup.find("button", string="Some text")
soup.find_all("button")

是不是多容易记忆~

实战练习

以上的全部内容就是这次带来的BeautifulSoup4的使用指南了,同样,经过学习之后,还是代码实战最容易掌握知识点,因此也准备了如下实战的代码

目标是: 结合之前爬取梨视频的高级爬取中的代码,将其中通过正则方式提取短视频标题和描述的正则表达式,替换为使用BeautifulSoup4的方式~

代码链接来自我女朋友的Github-2020-04-13