学习总结

书名《Python3 网络爬虫开发实战》

学习视频https://www.bilibili.com/video/BV1Pg4y1z7Lr

简介

解析库BeautifulSoup4和XPath一样都是解析用的,他不是完整的hmtl BeautifulSoup4也可以复原,他是在html和xml中查找信息的语言,他是通过标签属性来查询,Beautiful Soup 自动将输入文档转换为 Unicode 编码,输出文档转换为 UTF-8 编码

lxml 只 会 局 Object 部 遍 历 Model)的,会载入整个文档,解析整个 , Beautiful Soup 是 基 的
于 HTML DOM
DOM树,因此时间和内存开销都会大很多,所以性能要低于lxml。

Beautiful Soup 提取数据速度最慢

安装bs4

默认kali已经安装了

安装

1
pip3 install beautifulsoup4

解析器

BeautifulSoup4依赖解析器,它除了支持 Python 标准库中的 HTML 解析器外,还支持一些第三方解析器(比如 lxml)

image-20210830162235544

Python 标准库

Python 的内置标准库、执行速度适中、文档容错能力强

Python 2.7.3 及 Python 3.2.2 之前的版本文档容错能力差

1
BeautifulSoup(markup, "html.parser")

lxml HTML 解析器

速度快、文档容错能力强

需要安装 C 语言库

1
BeautifulSoup(markup, "lxml")

lxml XML 解析器

速度快、唯一支持 XML 的解析器

需要安装 C 语言库

1
BeautifulSoup(markup, "xml")

html5lib

最好的容错性、以浏览器的方式解析文档、生成 HTML5 格式的文档

速度慢、不依赖外部扩展

通过以上对比可以看出,lxml 解析器有解析 HTML 和 XML 的功能,而且速度快,容错能力强,所以推荐使用它。

如果使用 lxml,那么在初始化 Beautiful Soup 时,可以把第二个参数改为 lxml 即可

1
BeautifulSoup(markup, "html5lib")

指定使用那个解析库

格式

1
soup = BeautifulSoup('<p>Hello</p>', '要用的解析库的名字')

1
2
3
from bs4 import BeautifulSoup
soup = BeautifulSoup('<p>Hello</p>', 'lxml')
print(soup.p.string)

用法

下面提取出来的内容都是

语法bs4.element.Tag 类型的

获得节点下的内容

1
2
soup = BeautifulSoup(html, 'lxml')
soup.要获得的节点的名字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from bs4 import BeautifulSoup
html = \
"""
<div id="top">
<span class="position" width="350">职位名称</span>
<span>职位类别</span>
<li class="item-0">
<a href="link1.html">爬虫</a>
</li>
</div>
"""

# 此时就完成了 BeaufulSoup 对象的初始化
soup = BeautifulSoup(html, 'lxml')

print(soup.div)# 指定输出div下的全部内容

结果

1
2
3
4
5
6
7
<div id="top">
<span class="position" width="350">职位名称</span>
<span>职位类别</span>
<li class="item-0">
<a href="link1.html">爬虫</a>
</li>
</div>

image-20210830165021829

string属性显示数据内容

他只能取单个节点

语法

1
2
soup = BeautifulSoup(html, 'lxml')
soup.要获得的节点的名字.string

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from bs4 import BeautifulSoup
html = \
"""
<div id="top">
<span class="position" width="350">职位名称</span>
<span>职位类别</span>
<li class="item-0">
<a href="link1.html">爬虫</a>
</li>
</div>
"""

# 此时就完成了 BeaufulSoup 对象的初始化
soup = BeautifulSoup(html, 'lxml')

print(soup.span.string)# 指定输出span下的全部内容

print(soup.div.string) # 取父节点内容

结果可以看见只能取单节点内容

1
2
职位名称
None

image-20210901095919599

prettify()标准的缩进格式输出

他就是叫乱的html进行标准的缩进格式输出

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from bs4 import BeautifulSoup
html = \
"""
<div id="top">
<span class="position" width="350">职位名称</span>
<span>职位类别</span>
<li class="item-0">
<a href="link1.html">爬虫</a>
</li>
</div>
"""

# 此时就完成了 BeaufulSoup 对象的初始化
soup = BeautifulSoup(html, 'lxml')

print(soup.prettify()) # 标准的缩进格式输出

结果可以看见他的排序都是标准的,还有缩进

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<html>
<body>
<div id="top">
<span class="position" width="350">
职位名称
</span>
<span>
职位类别
</span>
<li class="item-0">
<a href="link1.html">
爬虫
</a>
</li>
</div>
</body>
</html>

name属性获取节点的名称

他就是提取你要提取的节点的名字

语法

1
2
soup = BeautifulSoup(html, 'lxml')
soup.要获取节点的名称.name

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from bs4 import BeautifulSoup
html = \
"""
<div id="top">
<span class="position" width="350">职位名称</span>
<span>职位类别</span>
<li class="item-0">
<a href="link1.html">爬虫</a>
</li>
</div>
"""

# 此时就完成了 BeaufulSoup 对象的初始化
soup = BeautifulSoup(html, 'lxml')

print(soup.span.name) # 属性获取节点的名称

结果

1
span

获取属性和值

attrs方法获取全部属性

他是字典的方式输出,里面的建是属性,值是属性值

语法

1
2
soup = BeautifulSoup(html, 'lxml')
print(soup.要取属性的节点名字.attrs)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from bs4 import BeautifulSoup
html = \
"""
<div id="top">
<img src="www.xxxx./a.png" alt="你好">
<li class="item-0">
<a href="link1.html">爬虫</a>
</li>
</div>
"""

# 此时就完成了 BeaufulSoup 对象的初始化
soup = BeautifulSoup(html, 'lxml')

print(soup.img.attrs) #获取所有属性

结果

1
{'src': 'www.xxxx./a.png', 'alt': '你好'}

attrs方法获取指定属性值

如果我想要那个值可以下面这样,用[]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from bs4 import BeautifulSoup
html = \
"""
<div id="top">
<img src="www.xxxx./a.png" alt="你好">
<li class="item-0">
<a href="link1.html">爬虫</a>
</li>
</div>
"""

# 此时就完成了 BeaufulSoup 对象的初始化
soup = BeautifulSoup(html, 'lxml')

print(soup.img.attrs['src']) #获取指定属性值

结果

1
www.xxxx./a.png

直接的方法获取指定属性值

其实获取属性的值可以直接下面这样

语法

1
2
soup = BeautifulSoup(html, 'lxml')
print(soup.要取属性的节点名字['要获得的属性名'])

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from bs4 import BeautifulSoup
html = \
"""
<div id="top">
<li class="item-0">
<a href="link1.html">爬虫</a>
</li>
</div>
"""

# 此时就完成了 BeaufulSoup 对象的初始化
soup = BeautifulSoup(html, 'lxml')

print(soup.img['src']) #获取指定属性值

结果:可以看见和上面的一样

1
www.xxxx./a.png

嵌套选择

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from bs4 import BeautifulSoup
html = \
"""
<div id="top">
<li class="item-0">
<a href="link1.html">爬虫</a>
</li>
</div>
"""

# 此时就完成了 BeaufulSoup 对象的初始化
soup = BeautifulSoup(html, 'lxml')

print(soup.div.li.a)

结果

1
<a href="link1.html">爬虫</a>

关联选择

取子节点 解释
contents 得到的结果是直接子节点的列表,返回的是一个列表
children 迭代器格式存储,返回的是一个迭代器
descendants 生成器格式存储,返回的是一个生成器
取父节点 解释
parent 取父节点,他返回的是bs4.element.Tag类型
parents 取全部父节点,他返回是的生成器类型
兄弟节点元素 解释
next_sibling 获得下一个兄弟节点,返回bs4
previous_sibling 获得上一个兄弟节点,返回bs4
next_siblings 获得下面的全部兄弟节点,返回生成器
previous_siblings 获得上面的全部兄弟节点,返回生成器

取子节点

contents列表的格式存储

contents 属性得到的结果是直接子节点的列表

属性得到的结果是直接子节点的列表

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from bs4 import BeautifulSoup
html =\
"""
<div id="top">
<img src="www.xxxx./a.png" alt="你好">
<li class="item-0">
<a href="link1.html">爬虫</a>
</li>
</div>
"""

# 此时就完成了 BeaufulSoup 对象的初始化
soup = BeautifulSoup(html, 'lxml')
print(soup.div.contents) # 结果以列表的格式输出

结果

1
2
3
['\n', <img alt="你好" src="www.xxxx./a.png"/>, '\n', <li class="item-0">
<a href="link1.html">爬虫</a>
</li>, '\n']

children迭代器格式存储

他返回的是一个迭代器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from bs4 import BeautifulSoup
html =\
"""
<div id="top">
<span class="position" width="350">职位名称</span>
<span>职位类别</span>
<li class="item-0">
<a href="link1.html">爬虫</a>
</li>
</div>
"""

# 此时就完成了 BeaufulSoup 对象的初始化
soup = BeautifulSoup(html, 'lxml')

print(soup.div.children) # 查看他的类型

for i in soup.div.children:
print(i)

结果:可以看见他是迭代器类的

1
2
3
4
5
6
<list_iterator object at 0x7fe49a745f70>
<span class="position" width="350">职位名称</span>
<span>职位类别</span>
<li class="item-0">
<a href="link1.html">爬虫</a>
</li>

descendants生成器格式存储

他返回的是一个生成器

descendants他和childrendescendants他会单独叫数据给显示出来

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from bs4 import BeautifulSoup
html =\
"""
<div id="top">
<span class="position" width="350">职位名称</span>
<span>职位类别</span>
<li class="item-0">
<a href="link1.html">爬虫</a>
</li>
</div>
"""

# 此时就完成了 BeaufulSoup 对象的初始化
soup = BeautifulSoup(html, 'lxml')

print(soup.div.descendants) # 结果以列表的格式输出

for i in soup.div.descendants:
print(i)

结果:可以看见他会叫单独的数据给显示出来

1
2
3
4
5
6
7
8
9
<span class="position" width="350">职位名称</span>
职位名称
<span>职位类别</span>
职位类别
<li class="item-0">
<a href="link1.html">爬虫</a>
</li>
<a href="link1.html">爬虫</a>
爬虫

取父节点

parent取父节点

他返回的是bs4.element.Tag类型的

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from bs4 import BeautifulSoup
html =\
"""
<div id="top">
<li class="item-0">
<a href="link1.html">爬虫</a>
</li>
</div>
"""

# 此时就完成了 BeaufulSoup 对象的初始化
soup = BeautifulSoup(html, 'lxml')

print(soup.a.parent)

结果:可以看见他取了a的父标签

1
2
3
<li class="item-0">
<a href="link1.html">爬虫</a>
</li>

### parents取全部父节点

他返回是的生成器类型的,

如果想获取所有的祖父节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

from bs4 import BeautifulSoup
html =\
"""
<div id="top">
<li class="item-0">
<a href="link1.html">爬虫</a>
</li>
</div>
"""

# 此时就完成了 BeaufulSoup 对象的初始化
soup = BeautifulSoup(html, 'lxml')

print(soup.a.parents)
for i in soup.a.parents:
print(i)

结果,可以看见结果会,逐个向父节点去获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<generator object PageElement.parents at 0x7f82a6bb59e0>
<li class="item-0">
<a href="link1.html">爬虫</a>
</li>
<div id="top">
<li class="item-0">
<a href="link1.html">爬虫</a>
</li>
</div>
<body><div id="top">
<li class="item-0">
<a href="link1.html">爬虫</a>
</li>
</div>
</body>
<html><body><div id="top">
<li class="item-0">
<a href="link1.html">爬虫</a>
</li>
</div>
</body></html>
<html><body><div id="top">
<li class="item-0">
<a href="link1.html">爬虫</a>
</li>
</div>
</body></html>

兄弟节点元素

就是获得下面这个

image-20210907174811497

获得兄弟节点属性 作用
next_sibling 获得下一个兄弟节点,返回bs4
previous_sibling 获得上一个兄弟节点,返回bs4
next_siblings 获得上面的全部兄弟节点,返回生成器
previous_siblings 获得下面的全部兄弟节点,返回生成器

next_sibling

获得下一个兄弟节点,返回bs4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

from bs4 import BeautifulSoup
html =\
"""
<div id="top">
1
<span class="position" width="350">职位名称</span>
2
<span>职位类别</span>
3
<li class="item-0">abc</li>
4
<span>职位类别</span>
5
<a href="link1.html">爬虫</a>
6
</div>
"""

# 此时就完成了 BeaufulSoup 对象的初始化
soup = BeautifulSoup(html, 'lxml')

print(soup.li.next_sibling) # 取li

结果

1
2

4

previous_sibling

获得上一个兄弟节点,返回bs4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

from bs4 import BeautifulSoup
html =\
"""
<div id="top">
1
<span class="position" width="350">职位名称</span>
2
<span>职位类别</span>
3
<li class="item-0">abc</li>
4
<span>职位类别</span>
5
<a href="link1.html">爬虫</a>
6
</div>
"""

# 此时就完成了 BeaufulSoup 对象的初始化
soup = BeautifulSoup(html, 'lxml')

print(soup.li.previous_sibling) # 取li

结果

1
2

3

next_siblings

获得下面的全部兄弟节点,返回生成器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

from bs4 import BeautifulSoup
html =\
"""
<div id="top">
1
<span class="position" width="350">职位名称</span>
2
<span>职位类别1</span>
3
<li class="item-0">abc</li>
4
<span>职位类别2</span>
5
<a href="link1.html">爬虫</a>
6
</div>
"""

# 此时就完成了 BeaufulSoup 对象的初始化
soup = BeautifulSoup(html, 'lxml')

print(list(soup.li.next_siblings)) # 取li,list函数强转成列表

结果

1
['\n    4\n    ', <span>职位类别1</span>, '\n    5\n    ', <a href="link1.html">爬虫</a>, '\n    6\n']

previous_siblings

获得上面的全部兄弟节点,返回生成器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

from bs4 import BeautifulSoup
html =\
"""
<div id="top">
1
<span class="position" width="350">职位名称</span>
2
<span>职位类别1</span>
3
<li class="item-0">abc</li>
4
<span>职位类别2</span>
5
<a href="link1.html">爬虫</a>
6
</div>
"""

# 此时就完成了 BeaufulSoup 对象的初始化
soup = BeautifulSoup(html, 'lxml')

print(list(soup.li.previous_siblings)) # 取li,list函数强转成列表

结果

1
['\n    3\n    ', <span>职位类别1</span>, '\n    2\n    ', <span class="position" width="350">职位名称</span>, '\n    1\n    ']

提取信息

其实其实用到的但是上面用过的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from bs4 import BeautifulSoup
html =\
"""
<div id="top">
<img src="www.xxxx./a.png" alt="你好">
<li class="item-0">
<a href="link1.html">爬虫</a>
</li>
</div>
"""

# 此时就完成了 BeaufulSoup 对象的初始化
soup = BeautifulSoup(html, 'lxml')
print(soup.a.string) # 返回内容
print(soup.a.attrs['href']) #返回属性内容

结果

1
2
爬虫
link1.html

方法选择器

前面都是通过属性来过滤的,如果遇见复杂的情况可以用下面这个方法进行过滤

find_all()

获取全部指定的标签

用到的参数是name,他返回是s4.element.ResultSet类型

格式

1
xxxx.find_all(name="要获取的标签的名字")

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from bs4 import BeautifulSoup
html =\
"""
<div>
<ul>
<li class="item-0">
<a href="link1.html">first item</a>
</li>
<li class="item-1">
</li>
second item
<li class="item-inactive">
<a href="link3.html">third item</a>
</li>

</ul>
</div>
"""

# 此时就完成了 BeaufulSoup 对象的初始化
soup = BeautifulSoup(html, 'lxml')

print(type(soup.find_all(name="a"))) # 查看他的类型
print(soup.find_all(name="a")) # 查看输出的结果

结果

1
2
<class 'bs4.element.ResultSet'>
[<a href="link1.html">first item</a>, <a href="link3.html">third item</a>]

查询属性

attrs参数

他类型是字典类型,返回是bs4.element.ResultSet

格式

1
xxxx.find_all(attrs={'id': 'list-1'})

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from bs4 import BeautifulSoup
html =\
"""
<div>
<ul>
<li class="item-0">
<a href="link1.html">first item</a>
</li>
<li class="item-1">
</li>
second item
<li class="item-inactive">
<a href="link3.html">third item</a>
</li>

</ul>
</div>
"""

# 此时就完成了 BeaufulSoup 对象的初始化
soup = BeautifulSoup(html, 'lxml')

print(type(soup.find_all(name="a",attrs={"href":"link1.html"}))) # 查看他的类型
print(soup.find_all(name="a",attrs={"href":"link1.html"})) # 查看输出的结果

结果

1
2
<class 'bs4.element.ResultSet'>
[<a href="link1.html">first item</a>]

不用参数

我们可以不用带参数,直接就href="link1.html"来过滤属性,可以叫属性值当参数用

格式

1
xxxx.find_all(href="link1.html")

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from bs4 import BeautifulSoup
html =\
"""
<div>
<ul>
<li class="item-0">
<a href="link1.html">first item</a>
</li>
<li class="item-1">
</li>
second item
<li class="item-inactive">
<a href="link3.html">third item</a>
</li>

</ul>
</div>
"""

# 此时就完成了 BeaufulSoup 对象的初始化
soup = BeautifulSoup(html, 'lxml')

print(type(soup.find_all(name="a",href="link1.html"))) # 查看他的类型
print(soup.find_all(name="a",href="link1.html")) # 查看输出的结果

结果

1
2
<class 'bs4.element.ResultSet'>
[<a href="link1.html">first item</a>]

text 参数

text 参数可以搜索内容,text 参数接受 字符串 , 正则表达式 , 列表, True

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import re
html='''
<div class="panel">
<div class="panel-body">
<a>a</a>
<a>b</a>
</div>
<a>1</a>
<a>2</a>
<div class="panel-body">
<a>c</a>
<a>d</a>
</div>
</div>
'''
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.find_all(text="a")) # 搜索有没有a
print(soup.find_all(text=["a","c"]))# 搜索有没有a和b
print(soup.find_all(text=re.compile("\d")))# 搜索全部匹配任意数字,等价于 `[0-9]`

结果

1
2
3
['a']
['a', 'c']
['1', '2']

find()方法

除了 find_all() 方法,还有 find() 方法,只不过后者返回的是单个元素,也就是第一个匹配的元素,而前者返回的是所有匹配的元素组成的列表

1
2


CSS 选择器