前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >​Python爬虫-BeautifulSoup详解

​Python爬虫-BeautifulSoup详解

作者头像
小一不二三
发布2019-12-31 16:47:06
1.4K0
发布2019-12-31 16:47:06
举报

作者:一叶

介绍:放不下灵魂的搬砖者

全文共3929字,阅读全文需15分钟

Python版本3.8.0,开发工具:Pycharm

上一节我们已经可以获取到网页内容,但是获取到的却是一长串的 html 代码,并不是我们想要的数据。那这一节,我们就来看看怎么去解析这些网页,轻松的拿到我们想要的数据。

首先网页解析有很多种解析工具,包括之前的正则表达式也可以用来解析(正则表达式如何使用),这节我们介绍通过BeautifulSoup4 进行网页解析。

官方链接奉上,https://beautifulsoup.readthedocs.io/zh_CN/latest/

安装BeautifulSoup4

  • 启动cmd
  • 输入pip3 install beautifulsoup4

pip3表示Python3版本,不需要区分版本直接使用pip

安装成功后截图如下:

BeautifulSoup4 快速开始

1. 导入bs4 库

代码语言:javascript
复制
from bs4 import BeautifulSoup

2. 创建beautifulsoup对象

先创建一个demo网页

代码语言:javascript
复制
 html = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title" name="dromouse"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1"><!-- Elsie --></a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>
and they lived at the bottom of a well.</p>
<p class="story">...</p>
</body>
</html>
"""

创建一个beautifulsoup对象

代码语言:javascript
复制
soup = BeautifulSoup(html)

或者通过读取本地HTML文件创建对象

代码语言:javascript
复制
soup = BeautifulSoup(open('demo.html'))

3. BeautifulSoup 将 HTML 文档转换成一个树形结构,每个节点都是 Python 对象,所有对象可以归纳为4种:

  • Tag
  • NavigableString
  • BeautifulSoup
  • Comment

(1)Tag

可以看做是HTML中的一个个标签,例如

代码语言:javascript
复制
<title>The Dormouse's story</title>
<b>The Dormouse's story</b>
<p class="story">Once upon a time there were three little sisters; </p>

上面的title、b、p 等 HTML 标签加上中间的内容就是一个 Tag,我们来试试如何通过 beautifulsoup 进行 Tag 内容获取

代码语言:javascript
复制
print(soup.title)
# 输出:<title>The Dormouse's story</title>
print(soup.head)
# 输出:<head><title>The Dormouse's story</title></head>
print(soup.p)
# 输出:<b>The Dormouse's story</b>

注:通过标签名只能查找到所有内容中第一个符合要求的标签

每个 tag 都有自己的 name,一个 tag 也会有多个属性 attrs 。tag 属性的操作方法和字典相同

代码语言:javascript
复制
print(soup.title.name)
# 输出:title
print(soup.p.attrs)
# 输出 {'class': ['title'], 'name':'dromouse'}

通过字典方式获取其中某一个属性

代码语言:javascript
复制
# 两种方式都可
print(soup.p.get('class'))
print(soup.p['class'])

(2)NavigableString

中文解释:可以遍历的字符串。

既然已经通过 Tag 获取到具体标签,那标签的内容就可以通过 NavigableString 拿到,使用方法特别简单:

代码语言:javascript
复制
# 获取标签内容
print(soup.p.string)

(3)BeautifulSoup

BeautifulSoup 对象表示的是一个文档的全部内容。大部分时候,可以把它当作是一个特殊的 Tag,我们可以分别获取它的名称、属性

代码语言:javascript
复制
print(soup.name)
print(soup.attrs)

(4)Comment

Comment 对象是一个特殊类型的 NavigableString 对象,输出的内容不包括注释符号。

例如:

代码语言:javascript
复制
print(soup.a)
print(soup.a.string)

输出:

代码语言:javascript
复制
<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>
 Elsie 

a 标签的内容实际上属于注释,利用 .string 来输出它的内容,我们发现它已经把注释符号去掉了。

实际上a 标签的内容属于注释,即 Comment。可以这样操作:

代码语言:javascript
复制
from bs4 import Comment
if type(soup.a.string) == Comment:
    print("comment:"+soup.a.string)

BeautifulSoup4数据查找提取

遍历文档树

通过 beautifulsoup 将 html 文档转换成树形结构,对文档树进行遍历

(1)节点内容

通过.string 属性输出节点内容

如果当前 tag 下没有标签,或者当前 tag 下只有一个子标签,则通过 .string 输出节点内容

代码语言:javascript
复制
# 当前 tag
<head>
    <title>The Dormouse's story</title>
</head>

print(soup.head.string)
print(soup.title.string)
# 输出相同
# The Dormouse's story

如果当前 tag下有不止一个标签,则会返回 None

代码语言:javascript
复制
print(soup.html.string)
# 输出:None

那么当前 tag下有多个标签,如果返回内容呢?用 .strings

使用 strings 需要遍历获取

代码语言:javascript
复制
for string in soup.strings:
    print(string)

使用 stripped_strings 去除多余空白内容

代码语言:javascript
复制
for string in soup.使用stripped_strings:
    print(string)
# 输出:
Once upon a time there were three little sisters; and their names were
,
Lacie
and
Tillie
and they lived at the bottom of a well.
...

(2)父节点

通过.parent 得到一个父节点,.parents 得到所有父节点

使用.parent 得到一个父节点

代码语言:javascript
复制
# 定位当前节点到title
current_tag = soup.head.title
# 输出当前节点的父节点
print(current_tag.parent.name)
# head

使用.parents 递归得到所有的父节点

代码语言:javascript
复制
# 定位当前节点到title
current_tag = soup.head.title
# 输出当前节点的所有父节点
for now_tag in current_tag.parents:
    print(now_tag.name)
# 输出
"""
head
html
[document]
"""

(3)子节点

.contents 可以将 tag 的子节点以列表的形式输出

代码语言:javascript
复制
print(soup.head.contents)
# 输出
# [<title>The Dormouse's story</title>]

.children 返回列表迭代器,通过循环获取每个 tag 的内容

代码语言:javascript
复制
for child in soup.body.children:
    print(child)

(4)兄弟节点

.next_sibling.previous_sibling 分别是获取同一个父节点下的下一个 tag/上一个 tag 节点(兄弟节点)。 如果节点不存在,返回 None

代码语言:javascript
复制
# p节点的下一个兄弟节点的上一个兄弟节点,等于p 本身
print(soup.body.p.next_sibling.previous_sibling.string)

.next_siblings.previous_siblings 属性可以对当前节点的兄弟节点迭代输出

代码语言:javascript
复制
for sibling in soup.body.p.next_sibling.previous_sibling:
    print(sibling.string)

(5)前后节点

.next_element.previous_element 属性指当前节点的下一个和上一个节点,不分层次关系

代码语言:javascript
复制
# 输出title 节点的下一个节点的内容
print(soup.title.next_element.string)
# 输出title 节点的上一个节点的内容
print(soup.title.previous_element.string)

.next_elements.previous_elements 属性可以对当前节点的所有下一个、上一个节点迭代输出

代码语言:javascript
复制
# 输出body 下p 节点的所有上一个节点
for current_tag in soup.body.p.previous_elements:
    print(current_tag.string)

# 输出
"""
The Dormouse's story
The Dormouse's story
The Dormouse's story
None
"""

搜索文档树

(1)find_all:搜索所有子节点,返回列表

find_all(name, attrs, recursive, text, limit, **kwargs):搜索当前tag的所有tag子节点,并判断是否符合过滤器的条件

  • name 参数

name 参数可以查找所有名字为 name 的tag,字符串对象会被自动忽略掉

name可以传多种功能参数。

  1. 传字符串:所有的字符串标签。 例如 'b' 代表 b 标签
  2. 传正则表达式:匹配所有符合正则表达式的标签。 例如 re.compile("^b") 匹配所有的 body 标签和 b 标签
  3. 传列表:查找所有在列表中的标签。 例如 ['a', 'b'] 代表所有 a 标签和 b 标签
  4. 传 True:True 表示可以匹配任何值,但是不会返回字符串节点
  5. 传方法:如果方法返回 True 则表示当前元素匹配且被找到,否则返回False
  • attrs 参数

如果一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当作指定名字 tag 的属性来搜索;

如果包含一个名字为 id 的参数, BeautifulSoup 会搜索每个 tag 的 ”id” 属性

代码语言:javascript
复制
# 搜索所有 id 为 link2 的子节点
soup.find_all(id='link2')
# 搜索所有 class 为 sister 的 a 节点
soup.find_all("a", class_="sister")
# 搜索所有 href 匹配到 elsie 的子节点
soup.find_all(href=re.compile('elsie'))
# 同时多个属性过滤
soup.find_all(id='link2', class_="sister", href=re.compile('elsie'))
  • recursive 参数

当只需要搜索当前节点的子节点,不需要搜索孙节点,需要设置 recursive=False

代码语言:javascript
复制
# 遍历 html 节点的所有节点
print(soup.html.find_all(name="title"))
# 只遍历 html 节点的所有子节点
print(soup.html.find_all(name="title", recursive=False))
# 输出
"""
[<title>The Dormouse's story</title>]
[]
"""
  • text 参数

text 参数可以搜文档中的字符串内容与 text一样。

text 参数接受字符串, 正则表达式, 列表, True

代码语言:javascript
复制
print(soup.find_all(text="Elsie"))
# []
print(soup.find_all(text=["Tillie", "Elsie", "Lacie"]))
# ['Lacie', 'Tillie']
print(soup.find_all(text=re.compile("Dormouse")))
# ["The Dormouse's story", "The Dormouse's story"]
  • limit 参数

当文档树特别大,搜索一遍需要很久的时候,我们可以指定返回结果的数量,相当于sql 中的 limit 关键字

代码语言:javascript
复制
# 只输出两个 a 标签即可
soup.find_all(name='a', limit=2)

(2)find:搜索所有子节点,返回结果

find_all 返回所有子节点,且返回列表 find 只返回搜索到的第一个子节点

(3)find_parent:搜索父节点

find_parent 搜索当前节点的父节点 find_parents 搜索当前节点的所有父节点

(4)find_next_sibling:搜索此节点后的兄弟节点

find_next_sibling 搜索当前节点的下一个兄弟节点的第一个节点 find_next_siblings 搜索当前节点的下一个所有兄弟节点

(5)find_previous_sibling:搜索此节点前的兄弟节点

find_previous_sibling 搜索当前节点的上一个兄弟节点的第一个节点 find_previous_siblings 搜索当前节点的上一个所有兄弟节点

(6)find_all_next:搜索此节点后的所有节点

find_next 搜索当前节点的下一个节点的第一个节点 find_all_next 搜索当前节点的下一个所有节点

(7)find_all_previous:搜索此节点前的所有节点

find_all 搜索当前节点的上一个节点的第一个节点 find_all_previous 搜索当前节点的撒和那个一个所有节点

(2)-(7)的参数与(1)相同,按照(1)的原理应用即可


css选择器

我们在写 CSS 时,标签名不加任何修饰,类名前加点,id名前加 #

在这里我们也可以利用类似的方法来筛选元素,用到的方法是 soup.select(),返回类型是 list

(1)通过标签名查找

查找所有找到的结果,返回 list

代码语言:javascript
复制
# 查找title标签
print(soup.select('title')) 
# 查找 a 标签
print(soup.select('a'))

(2)通过类名查找

代码语言:javascript
复制
# 查找 class 是 sister 的所有结果
print(soup.select('.sister'))

(3)通过 id 名查找

代码语言:javascript
复制
# 查找 id 为 link1 的所有结果
print(soup.select('#link1'))

(4)组合查找

代码语言:javascript
复制
# 查找 p 标签中, id 为 link1 的所有结果
print(soup.select('p #link1'))
# 查找 p 标签中, class 为 sister 的所有结果
print(soup.select('p .sister'))
# 子标签查找
print(soup.select('body > p'))

# 组合查找
# body 标签下的 class 为 story 的标签下的 id 为 link1 的所有结果
print(soup.select('body .story #link1'))

(5)属性查找

查找时还可以加入属性元素,属性需要用中括号括起来。 注意属性和标签属于同一节点,所以中间不能加空格,否则会无法匹配到。

代码语言:javascript
复制
# 查找 id 为 link1 的所有结果
print(soup.select('a[id="link1"]'))
#  查找 p 标签下的 id 为 link2 的 a 标签
print(soup.select('p a[id="link2"]'))

# 输出 id 为 link 的 a 标签的内容
print(soup.select('a[id="link2"]')[0].string)

css 选择其实和 find_all 函数的本质是一样的,选择合适的使用吧


这篇大家可以收藏起来,以后用到的时候翻出来看一下。

这篇讲了利用 beautifulsoup 进行网页解析,主要介绍了它的查找功能,其实还有修改删除功能没有提到,不过我觉得在爬虫过程中,我们使用查找搜索会更频繁些,掌握今天提到的这些即可。

Python系列

Python系列会持续更新,从基础入门到进阶技巧,从编程语法到项目实战。若您在阅读的过程中发现文章存在错误,烦请指正,非常感谢;若您在阅读的过程中能有所收获,欢迎一起分享交流。

如果你也想和我一起学习Python,关注我吧!

学习Python,我们不只是说说而已

End

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-12-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 知秋小梦 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 安装BeautifulSoup4
  • BeautifulSoup4 快速开始
  • BeautifulSoup4数据查找提取
  • 搜索文档树
  • css选择器
  • Python系列
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档