『Python工具篇』Beautiful Soup 解析网页内容

在 [《『Python爬虫』极简入门》] 里介绍了写一个爬虫程序的基础原理:

  1. 爬取数据
  2. 解析数据
  3. 存储数据

而在解析数据时使用的是 Beautiful Soup 这个库,直译过来就是“靓汤”,这是广东人最喜欢的库。

Beautiful Soup 的作用是解析爬取回来的网页数据,也就是解读 HMTL 内容。

对于前端开发者来说,这类解析网页内容的工具其实有点像 CSS 选择器,所以前端开发者学起来会非常快。

我也会以前端的角度去讲解 Beautiful Soup

本文使用的编辑器是 Jupyter Notebook,这个编辑器对于学习 Python 来说非常好用

安装和引入

Beautiful Soup 不是 Python 的内置库,所以使用之前需要先安装和引入。

安装

bash

pip install beautifulsoup4

引入

python

from bs4 import BeautifulSoup

基础用法

解析器

Beautiful Soup 中,解析器的作用是将原始的 HTMLXML 文档解析成一个树形结构,以便于我们可以方便地浏览、搜索和修改其中的元素。解析器负责解析标记语言中的标签、属性和文本,并将其转换成一个可以被程序操作的数据结构,比如树形结构或者 DOM 树。这样我们就可以通过编程的方式来访问、提取和操作网页中的数据了。

不同类型的文档可能需要不同的解析器来处理,因为它们可能具有不同的语法、结构和特性。在选择解析器时,通常会考虑解析速度、性能、准确性以及适用的文档类型等因素。

Beautiful Soup 支持几种解析器,其中一种是 Python 标准库中的 HTML 解析器,另外还支持第三方的 lxml parserhtml5lib

引用 Beautiful Soup 官方文档对解释器的介绍:

解析器使用方法优势劣势
Python 标准库BeautifulSoup(markup, "html.parser")- Python的内置标准库- 执行速度较快- 容错能力强- 速度没有 lxml 快,容错没有 html5lib强
lxml HTML 解析器BeautifulSoup(markup, "lxml")- 速度快- 容错能力强- 额外的 C 依赖
lxml XML 解析器BeautifulSoup(markup, ["lxml-xml"])``BeautifulSoup(markup, "xml")- 速度快- 唯一支持 XML 的解析器- 额外的 C 依赖
html5libBeautifulSoup(markup, "html5lib")- 最好的容错性- 以浏览器的方式解析文档- 生成 HTML5 格式的文档- 速度慢- 额外的 Python 依赖

官方推荐使用 lxml 来获得更高的速度。

也就是这么用:

python

BeautifulSoup('<div>雷猴</div>', 'lxml')

到此,相信各位工友对于 BeautifulSoup 的用法还是有点懵的。没关系,先知道有这几种解析器,接下来的内容会开始讲解用法。

自动补全

如果把缺少闭合标签的 HTML 代码丢给 BeautifulSoup 解析, BeautifulSoup 会自动补全闭合标签。

python
from bs4 import BeautifulSoup

html = """
<ul>
		<li>
        <span>雷猴</span>
    </li>
	<li>
        <span>鲨鱼辣椒
    </li>
"""

soup = BeautifulSoup(html, 'lxml')
print(soup)

输出结果:

css

<html><body><ul>
<li>
<span>雷猴</span>
</li>
<li>
<span>鲨鱼辣椒
    </span></li>
</ul></body></html>

在上面这个例子中,“鲨鱼辣椒”后面少了一个 </span> ,也少了最后一个 </ul>。当把这段 HTML 代码丢给 BeautifulSoup 解析后,它会自动帮我们把这两个标签补全,同时也会将 <body><html> 标签给补全。

标签选择器

HTML 里的标签有 <h1><div><span><a> 等一大堆。这些都叫标签。当我们获取到一段 HTML 代码后,用 BeautifulSoup 提供的标签选择器(也叫节点选择器)就可以提取出对应标签的内容。

先来看看代码

python

from bs4 import BeautifulSoup

html = """
<ul>
		<li>
        <span>雷猴</span>
    </li>
	<li>
        <span>鲨鱼辣椒</span>
    </li>
</ul>
"""

soup = BeautifulSoup(html, 'lxml')
print(soup.li)

输出结果:

css

<li>
<span>雷猴</span>
</li>

这段 HTML 代码中有多个 <li> 标签,BeautifulSoup 的标签选择器只会选中第一个匹配的节点,后面的同名节点全部会忽略掉。

上面这段代码我们使用的是自己写好的一段 HTML 文本,我们也可以使用 requests 将互联网上的页面请求下来解析,比如这么做:

python

import requests
from bs4 import BeautifulSoup

# 请求 http://books.toscrape.com/
res = requests.get ("http://books.toscrape.com/")
resHTML = res.text

# 将请求回来的页面丢给 BeautifulSoup 解析
soup = BeautifulSoup(resHTML, 'lxml')

# 输出这个页面中的第一个 li 标签的内容
print(soup.li)

输出结果:

xml

<li>
<a href="index.html">Home</a>
</li>

获取文本内容

前面的“标签选择器”例子中,获取了 <li> 标签的内容里包含里 <span> 标签。如果只想要 <li> 标签里的文本内容,而且不包含 <span> 标签的话可以用 text 属性获取。

python

html = """
<ul>
		<li>
        <span>雷猴</span>
    </li>
</ul>
"""

soup = BeautifulSoup(html, 'lxml')
print(soup.li.text)

此时打印的内容是"雷猴"。

除了 text 外,还可以使用 string 属性获取文本内容

python

html = """
<ul>
	<li>
        <span>雷猴</span>
    </li>
</ul>
"""

soup = BeautifulSoup(html, 'lxml')
print(soup.span.string)

此时还是输出“雷猴”,但需要注意的是,前面使用 text 的标签是 <li> ,而这里使用 string 的标签是 <span>

textstring 是有区别的,text 支持从多节点中提取文本信息,而 string 只支持从单节点中提取文本信息。

获取标签名

通过 name 属性可以获取节点的名称。

python

html = """
<ul>
		<li>
        <span>雷猴</span>
    </li>
</ul>
"""

soup = BeautifulSoup(html, 'lxml')
print(soup.span.name)

输出:span

这个例子看上去好像有点多此一举,通过 span.name 获取 span 节点的名字,就像问“先生请问你的性别是?”。

但其实它也是有用的,比如通过其他查询条件获取到的内容你是不知道它们用了什么标签的,此时就可以通过 name 属性查出来了。

获取标签的属性

什么是属性?拿下面这段 HTML 代码举例。

html

<a
    href="https://juejin.cn/post/7346483502172717066"
    id="a1"
    class="link1 highlight"
>『Python爬虫』极简入门</a>

在这个 <a> 标签中有3个属性,分别是 hrefidclass

Beautiful Soup 里可以通过 attrs 一次获取这些属性。

python

html = """
<a
    href="https://juejin.cn/post/7346483502172717066"
    id="a1"
    class="link1 highlight"
>『Python爬虫』极简入门</a>
"""

soup = BeautifulSoup(html, 'lxml')
print(soup.a.attrs)

输出结果:

rust

{
	'href': 'https://juejin.cn/post/7346483502172717066',
	'id': 'a1',
	'class': ['link1', 'highlight']
}

也可以通过指定的属性名获取。

python

# 省略部分代码

print(soup.a.attrs['href'])
print(soup.a.attrs['id'])
print(soup.a.attrs['class'])

输出:

arduino

https://juejin.cn/post/7346483502172717066
a1
['link1', 'highlight']

attrs 返回的值有时候是字符串,有时候是列表,其原因是有些属性确实是字符串就能表示了,而像 class 这种属性是可以存放多个值的,这种情况就使用列表。

上面获取指定属性的写法还是有点复杂,可以简化成这样。

python

# 省略部分代码

# 以下两句的输出结果是一样的
print(soup.a.attrs['href'])
# 简化版
print(soup.a['href'])

嵌套选择

可以通过嵌套选择的方式精准选择元素。

python

html = """
<ul>
    <li>雷猴</li>
</ul>
<ol>
    <li>鲨鱼辣椒</li>
</ol>
"""

soup = BeautifulSoup(html, 'lxml')
print(soup.ol.li)

输出结果:

css

<li>鲨鱼辣椒</li>

通过 soup.ol.li 选择了 <ol> 里面的 <li>

子选择器

CSS 中,子选择器使用 “>” 符号,它选择某个元素的直接子元素,而不包括孙子元素及更深层次的后代元素。这意味着子选择器只会选择目标元素的直接子元素,不会选择其后代元素。

例如:

html

<div id="parent">
    <p>第一个段落</p>
    <div>
        <p>第二个段落</p>
    </div>
    <p>第三个段落</p>
</div>

我们使用子选择器 #parent > p,它将选择 id 为 “parent” 的 div 元素下的直接子元素 p,即第一个段落和第三个段落,而不会选择第二个段落,因为第二个段落是位于 div 的子元素的子元素。

而在 BeautifulSoup 中可以使用 contents 属性获取某元素的直接子元素。

python

html = """
<ul>
    <li>雷猴</li>
    <li>鲨鱼辣椒</li>
</ul>
"""

soup = BeautifulSoup(html, 'lxml')
print(soup.ul.contents)

输出结果:

javascript

['\n', <li>雷猴</li>, '\n', <li>鲨鱼辣椒</li>, '\n']

除此之外,还可以使用 children 属性获取子元素,它返回的是一个生成器类型,需要遍历才能获取到里面的值。

python

html = """
<ul>
    <li>雷猴</li>
    <li>鲨鱼辣椒</li>
</ul>
"""

soup = BeautifulSoup(html, 'lxml')
lis = soup.ul.children
for i, child in enumerate(lis):
    print(f"{i} => {child}")

输出结果:

ini

0 => 

1 => <li>雷猴</li>
2 => 

3 => <li>鲨鱼辣椒</li>
4 => 

不管是用 contents 还是 children 都能看到筛选出一些“空行”,这是因为上面那段 HTML 里确实有换行。

后代选择器

使用 descendants 属性可以获取某元素的所有后代元素。

python

html = """
<div>
<ul>
    <li>雷猴</li>
    <li>鲨鱼辣椒</li>
</ul>
</div>
"""

soup = BeautifulSoup(html, 'lxml')
for i, child in enumerate(soup.div.descendants):
    print(f"{i} => {child}")

输出结果:

ini

0 => 

1 => <ul>
<li>雷猴</li>
<li>鲨鱼辣椒</li>
</ul>
2 => 

3 => <li>雷猴</li>
4 => 雷猴
5 => 

6 => <li>鲨鱼辣椒</li>
7 => 鲨鱼辣椒
8 => 

9 => 

descendants 返回的结果是生成器类型的,需要遍历输出。

父选择器

使用parent 属性可以获取直接父元素。

python

html = """
<div>
    <ul>
        <li>雷猴</li>
        <li>鲨鱼辣椒</li>
    </ul>
</div>
"""

soup = BeautifulSoup(html, 'lxml')
print(soup.li.parent)

输出结果:

python

<ul>
<li>雷猴</li>
<li>鲨鱼辣椒</li>
</ul>

祖先选择器

使用 parents 属性可以获取祖先节点,爸爸的爸爸级别的元素也能获取到。

python

html = """
<div>
    <ul>
        <li>雷猴</li>
        <li>鲨鱼辣椒</li>
    </ul>
</div>
"""

soup = BeautifulSoup(html, 'lxml')
print(list(enumerate(soup.li.parents)))

输出结果:

javascript

[(0, <ul>
<li>雷猴</li>
<li>鲨鱼辣椒</li>
</ul>), (1, <div>
<ul>
<li>雷猴</li>
<li>鲨鱼辣椒</li>
</ul>
</div>), (2, <body><div>
<ul>
<li>雷猴</li>
<li>鲨鱼辣椒</li>
</ul>
</div>
</body>), (3, <html><body><div>
<ul>
<li>雷猴</li>
<li>鲨鱼辣椒</li>
</ul>
</div>
</body></html>), (4, <html><body><div>
<ul>
<li>雷猴</li>
<li>鲨鱼辣椒</li>
</ul>
</div>
</body></html>)]

这里使用了 list() 方法,使 parents 返回的生成器类型数据装成列表类型。

兄弟选择器

兄弟选择器的作用是获取同级别的节点,一共有这4个属性供我们使用:

  1. next_sibling: 获取下一个兄弟节点
  2. previous_sibling: 获取上一个兄弟节点
  3. next_siblings: 获取后面的所有兄弟节点
  4. previous_siblings: 获取前面的所有兄弟节点

演示一下:

python

html = """
<h1>标题1</h1><h2>标题2</h2><h3>标题3</h3><h4>标题4</h4><h5>标题5</h5><h6>标题6</h6>
"""

soup = BeautifulSoup(html, 'lxml')
print(f"next sibling: {soup.h3.next_sibling}")
print(f"previous sibling: {soup.h3.previous_sibling}")
print(f"next siblings: {list(enumerate(soup.h3.next_siblings))}")
print(f"previous siblings: {list(enumerate(soup.h3.previous_siblings))}")

输出结果:

javascript

next sibling: <h4>标题4</h4>
previous sibling: <h2>标题2</h2>
next siblings: [(0, <h4>标题4</h4>), (1, <h5>标题5</h5>), (2, <h6>标题6</h6>), (3, '\n')]
previous siblings: [(0, <h2>标题2</h2>), (1, <h1>标题1</h1>)]

方法选择器

前面介绍的方法都是基于标签名(节点名)来选择的,但这不够灵活。

如果你想通过属性名等条件选择标签,可以使用 find_allfind 方法。

举个例子,我要获取所有 <li> 标签

python

html = """
<ul>
    <li name="n1">雷猴</li>
    <li name="n1">鲨鱼辣椒</li>
    <li name="n2">蝎子莱莱</li>
    <li name="n2">蟑螂恶霸</li>
</ul>
"""

soup = BeautifulSoup(html, 'lxml')
print(soup.find_all(name="li"))

输出结果:

javascript

[<li name="n1">雷猴</li>, <li name="n1">鲨鱼辣椒</li>, <li name="n2">蝎子莱莱</li>, <li name="n2">蟑螂恶霸</li>]

通过 name="li" 作为条件筛选出所有 <li> 标签出来。

但这几个 <li> 都有一个 name 属性,如果想筛选出属性 namen1 的所有 <li> 标签,需要用前面提到的 attrs 来获取。

python

html = """
<ul>
    <li name="n1">雷猴</li>
    <li name="n1">鲨鱼辣椒</li>
    <li name="n2">蝎子莱莱</li>
    <li name="n2">蟑螂恶霸</li>
</ul>
"""

soup = BeautifulSoup(html, 'lxml')
print(soup.find_all(attrs={"name": "n1"}))

输出结果:

javascript

[<li name="n1">雷猴</li>, <li name="n1">鲨鱼辣椒</li>]

还可以通过 idclass 当做过滤条件。

python

html = """
<ul>
    <li class="li_1">雷猴</li>
    <li class="li_1">鲨鱼辣椒</li>
    <li class="li_2">蝎子莱莱</li>
    <li class="li_2">蟑螂恶霸</li>
</ul>
"""

soup = BeautifulSoup(html, 'lxml')
print(soup.find_all(attrs={"class": "li_2"}))

输出结果:

javascript

[<li class="li_2" name="n2">蝎子莱莱</li>, <li class="li_2" name="n2">蟑螂恶霸</li>]

但像 idclass 这两个常用的属性,可以不使用 attrs但是,classpython 的关键字,如果要当做 CSS 的类选择器需要用 class_="xxx" 的方式去书写,也就是 class 后面加多一个下划线。

python

html = """
<ul>
    <li class="li_1" id="li1">雷猴</li>
    <li class="li_1" id="li2">鲨鱼辣椒</li>
    <li class="li_2" id="li3">蝎子莱莱</li>
    <li class="li_2" id="li4">蟑螂恶霸</li>
</ul>
"""

soup = BeautifulSoup(html, 'lxml')
print(soup.find_all(class_="li_2"))
print(soup.find_all(id="li2"))

输出结果:

bash

[<li class="li_2" id="li3">蝎子莱莱</li>, <li class="li_2" id="li4">蟑螂恶霸</li>]
[<li class="li_1" id="li2">鲨鱼辣椒</li>]

再来,find_all() 还支持通过文本内容来匹配节点,这个可牛了!

但匹配文本需要使用正则表达式。

python

import re
from bs4 import BeautifulSoup

html = """
<ul>
    <li>雷猴辣椒</li>
    <li>鲨鱼辣椒</li>
    <li>蝎子莱莱</li>
    <li>蟑螂恶霸</li>
</ul>
"""

soup = BeautifulSoup(html, 'lxml')
print(soup.find_all(string=re.compile("辣椒")))

输出结果:

css

['雷猴辣椒', '鲨鱼辣椒']

介绍完 find_all() 再回头看 find(),其实它们的用法都是一样的,只是返回的结果不同而已。

find() 方法返回的是单个元素(节点),会返回第一个匹配到的元素。

用法和 find_all() 一样,这里就不重复讲述了。

CSS选择器

Beautiful Soup 支持使用 CSS 选择器,只需调用 select 方法,然后像写 CSS 那样把选择器传进去就可以了。

python

import re

html = """
<ul class="ul_x">
    <li class="list">雷猴辣椒</li>
    <li class="list">鲨鱼辣椒</li>
    <li class="list">蝎子莱莱</li>
    <li class="list">蟑螂恶霸</li>
</ul>
"""

soup = BeautifulSoup(html, 'lxml')
print(soup.select(".ul_x .list"))

输出结果:

javascript

[<li class="list">雷猴辣椒</li>, <li class="list">鲨鱼辣椒</li>, <li class="list">蝎子莱莱</li>, <li class="list">蟑螂恶霸</li>]

好啦,常用的方法介绍完了,想进修的工友可以看看 Beautiful Soup 官网

呕心沥血整理出来和视频想配套的笔记文档,搭配起来,学习事半功倍,赶紧收藏起来!!!

一丶90部Python基础视频教程

基础很重要,打好基础事半功倍,只要基础打的好,才能顺利的更深层次的深挖

主要分为
1.Python简介和第一个程序以及错误排查
2.算数运算符
3.函数基础
4.异常
5.网络通信
6.面向对象
7.高级变量类型
8.pycharm简单使用
9.单行注释与多行注释
10.变量的基本使用以及input函数说明
11.模块与包
12.数据的格式化输出
13.if条件判断与random函数使用
14.变量的命名规则
15.Python运算符比较和循环

2丶Python配套预习笔记PDF版

从11个模块,让你全方位的掌握,主要分为:
1.初识Python
2.大数据体系内容
3.AI研发
4.全栈开发
5.Python核心编程
6.并发编程
7.数据库编程
8.爬虫入门
9.源码框架
10.JS逆向
11.Android逆向

3丶Python大礼包

一些常用的Python收藏,实战项目,练习题和电子书等

4丶用Python实现一些休闲小游戏

用Python实现一些休闲小游戏,让你成为工作之余的工作(摸鱼)小能手,有源码分享,小伙伴们可以自行参考自己玩一下

5丶爬虫教程

20个实战项目,手把手教你

6丶29个视频和代码

上述所有资料 ⚡️ ,朋友们如果有需要 📦《全套Python学习资料》的,可以扫描下方二维码免费领取 🆓
😝有需要的小伙伴,可以点击下方链接免费领取或者V扫描下方二维码免费领取🆓

👉CSDN大礼包🎁:全网最全《Python学习资料》免费分享(安全链接,放心点击)👈

好的,我可以为你解答这个问题。Beautiful Soup 是一个 Python 的第三方库,主要用于解析 HTML 和 XML 的文档。使用 Beautiful Soup 可以轻松地获取网页中的各种信息,例如标签、属性、文本等等。 下面是使用 Beautiful Soup 解析网页的基本步骤: 1. 安装 Beautiful Soup 库。可以使用 pip 工具来安装,命令如下: ``` pip install beautifulsoup4 ``` 2. 导入 Beautiful Soup 库。在 Python 代码中,可以使用以下语句导入 Beautiful Soup 库: ```python from bs4 import BeautifulSoup ``` 3. 获取网页内容。可以使用 Python 的 requests 库来获取网页内容,例如: ```python import requests url = 'https://www.example.com' response = requests.get(url) html = response.text ``` 4. 使用 Beautiful Soup 解析网页。可以使用以下语句来创建一个 Beautiful Soup 对象,并解析网页内容: ```python soup = BeautifulSoup(html, 'html.parser') ``` 其中,第一个参数是网页内容,第二个参数是解析器类型,这里使用的是 Python 的内置解析器。 5. 提取网页中的信息。使用 Beautiful Soup 提供的各种方法,可以轻松地提取网页中的各种信息,例如标签、属性、文本等等。例如: ```python # 获取网页中的标题 title = soup.title.string # 获取所有的链接 links = soup.find_all('a') # 获取第一个 div 标签的 class 属性值 div_class = soup.find('div')['class'] ``` 这就是使用 Beautiful Soup 解析网页的基本步骤。当然,在实际应用中,还需要根据具体的需求来选择相应的方法和参数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值