我们通过Requests请求url获取数据,请求把数据返回来之后就要提取目标数据,不同的网站返回的内容通常有多种不同的格式,一种是 json 格式,我们可以直接通过json.loads转换python的json对象处理。另一种 XML 格式的,还有一种最常见格式的是 HTML 文档,今天就来讲讲如何从 HTML 中提取出感兴趣的数据。
BeautifulSoup 是一个用于解析 HTML 文档的 Python 库,通过 BeautifulSoup,你只需要用很少的代码就可以提取出 HTML 中任何感兴趣的内容,此外,它还有一定的 HTML 容错能力,对于一个格式不完整的HTML 文档,它也可以正确处理。
安装 beautifulsoup
pip install beautifulsoup4
初始化对象时可以直接传递字符串或者文件句柄
soup = BeautifulSoup(open("index.html"))
soup = BeautifulSoup("<html>data</html>")
支持多种解析接口
# python内置HTML解析
BeautifulSoup(markup, "html.parser")
# lxml语言支持HTML解析
BeautifulSoup(markup, "lxml")
# 解析XML引擎
BeautifulSoup(markup, "xml")
# 解析HTML5引擎
BeautifulSoup(markup, "html5lib")
自动添加和补全标签
下面是一段不规范的html,缺少闭合标签
html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><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>
"""
- 它由很多标签(Tag)组成,比如 html、head、title等等都是标签
- 一个标签对构成一个节点,比如 …是一个根节点
- 节点之间存在某种关系,比如p之间互为邻居,他们是相邻的兄弟(sibling)节点
- p 是 body 的直接子(children)节点,还是 html 的子孙(descendants)节点
- body 是 p 的父(parent)节点,html 是 p 的祖辈(parents)节点
- 嵌套在标签之间的字符串是该节点下的一个特殊子节点,比如title文本内容“The Dormouse’sstory”也是一个节点,只不过没名字。
from bs4 import BeautifulSoup
soup = BeautifulSoup(html_doc, 'html.parser')
print(soup.prettify())
prettify()标准缩进格式的输出。输出内容如下:
<html>
<head>
<title>
The Dormouse's story
</title>
</head>
<body>
<p class="title">
<b>
The Dormouse's story
</b>
</p>
<p class="story">
Once upon a time there were three little sisters; and their names were
<a class="sister" href="http://example.com/elsie" id="link1">
Elsie
</a>
,
<a class="sister" href="http://example.com/lacie" id="link2">
Lacie
</a>
and
<a class="sister" href="http://example.com/tillie" id="link2">
Tillie
</a>
; and they lived at the bottom of a well.
</p>
<p class="story">
...
</p>
</body>
</html>
bs4获取标签及内容示例
# title标签
soup.title
# <title>The Dormouse's story</title>
# title标签名称
soup.title.name
# 'title'
# # title标签的文本字符内容
soup.title.string
# 'The Dormouse's story'
# title标签父节点名称
soup.title.parent.name
# 'head'
# 从前向后找到html孙节点第一个p节点
soup.p
# <p class="title"><b>The Dormouse's story</b></p>
# p节点的class属性
soup.p['class']
# ['title']
# 进栈出栈的方式找到第一个a标签
soup.a
# <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
# p节点的href属性
soup.a["href"]
# 'http://example.com/elsie'
soup.find_all('a')
# 同上
soup.find_all("p")[1].find_all("a")
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
soup.find(id="link3")
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
遍历文档树
从根节点 html 标签开始遍历,元素进栈出栈,直到找到目标元素为止。
BeatifulSoup 将 HTML 抽象成为 4 类主要的数据类型:
- Tag:每个标签节点就是一个Tag对象
- NavigableString:包裹在Tag对象里的文本字符串
- BeautifulSoup 对象代表要解析的整个
- Comment注释对象
type(soup)
# <class 'bs4.BeautifulSoup'>
type(soup.p)
# <class 'bs4.element.Tag'>
# type(soup.p.string)
<class 'bs4.element.NavigableString'>
Tag标签
每个 Tag 都有一个名字,它对应 HTML 的标签名称。
soup.p.name
# 'p'
标签有属性,属性的访问方式和字典是类似的,它返回一个列表对象或字符串。
soup.p['class']
# ['title']
soup.a['href']
# 'http://example.com/elsie'
NavigableString
获取标签中的内容,直接使用 .stirng 即可获取,它是一个 NavigableString 对象 。
soup.p.string
# "The Dormouse's story"
type(soup.p.string)
bs4.element.NavigableString
搜索文档树
搜索文档树是通过指定标签名来搜索元素,还可以通过指定标签的属性值来精确定位某个节点元素,最常用的两个方法就是 find 和 find_all。这两个方法在 BeatifulSoup 和 Tag 对象上都可以被调用。
find_all()方法
find_all( name , attrs , recursive , text , **kwargs )
第一个参数 name 是标签节点的名字。
# 所有p标签
soup.find_all("p")
# [<p class="title"><b>The Dormouse's story</b></p>,
# <p class="story">Once upon a time there were three little sisters; and their names were
# <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;
# and they lived at the bottom of a well.</p>,
# <p class="story">...</p>]
第二个参数是标签的class属性值
soup.find_all("p","title")
# 同上
soup.find_all("p",class_ ="title")
# [<p class="title"><b>The Dormouse's story</b></p>]
kwargs 是标签的属性名值对。
import re
# 支持使用标签属性
soup.find_all(href="http://example.com/lacie")
soup.find_all(id="link2")
# 支持使用正则
soup.find_all(href=re.compile("lacie"))
# [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
# 支持使用布尔类型
soup.find_all('a',id=True)
遍历和搜索相结合,先定位到 body 标签,再从 body 中找 a 标签.。
soup.body.find_all('a',id=True)
find()方法
find 方法跟 find_all 类似,唯一不同的地方是,它返回的单个 Tag 对象而非列表,如果没找到匹配的节点则返回 None。如果匹配多个 Tag,只返回第0个。
soup.body.find("a")
# <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
get_text()方法
获取标签里面内容,除了可以使用 .string 之外,还可以使用 get_text 方法,不同的地方在于前者返回的一个 NavigableString 对象,后者返回的是 字符串。
soup.body.find("a").get_text()
# Elsie
实际场景中我们一般使用 get_text 方法获取标签中的内容。
总结:
通过beautifulsoup我们能够解析大部分静态html网页,遍历和搜索组合方式定位html的标签,并获取相应标签的内容。