Beautiful Soup 简称 BS4(其中 4 表示版本号)是一个 Python 第三方库,是一个用于解析HTML和XML文档的Python库,主要用于从网页中提取数据。它能够将HTML或XML文档转换为一个树形结构,使得用户可以通过编程方式方便地访问、提取和操作网页中的数据。
图:BS4官网LOGO图
一、下载安装
Beautiful Soup不是Python的内置库,因此在使用前需要安装。可以通过以下命令安装
pip install beautifulsoup4
阿里国内源安装:
pip install beautifulsoup4 -i http://mirrors.aliyun.com/pypi/simple --trusted-host mirrors.aliyun.com
此外,还可以安装其他解析器如html5lib
:
pip install html5lib
二、主要功能和使用方法
- 解析器:Beautiful Soup支持多种解析器,包括Python标准库中的HTML解析器以及第三方库如
lxml
和html5lib
。推荐使用lxml
,因为它速度快且功能强大。 - 创建对象:通过BeautifulSoup类可以创建对象,该对象代表整个HTML文档树。可以通过各种方法(如
find()
和find_all()
)来访问文档的各个部分。 - 查找和提取数据:可以使用
find()
方法查找文档中符合条件的第一个元素,使用find_all()
方法查找所有符合条件的元素。这些方法支持通过标签名、属性等多种方式定位元素。 - 处理文本内容:BeautifulSoup提供了处理文本内容的方法,如获取标签中的文本内容(
NavigableString
)和剥离标签但保留文本的字符串(stripped_strings
)。
示例代码
以下是一个简单的示例,展示如何使用BeautifulSoup解析HTML文档并提取信息:
from bs4 import BeautifulSoup
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>
"""
soup = BeautifulSoup(html_doc, 'lxml')
title_tag = soup.find('b') # 查找第一个<b>标签
print(title_tag.text) # 输出: 'The Dormouse's story'
bs4还支持一些第三方解析器:
解析器 | 使用方法 | 优势 | 劣势 |
---|---|---|---|
Python标准库 | BeautifulSoup(html,’html.parser’) | Python内置标准库;执行速度快 | 在python2.7.3和3.2.2之前的版本中文档容错能力差 |
lxml html解析器 | (需要额外导入)BeautifulSoup(html,’lxml’) | 速度快;容错能力强 | 需要安装C语言库 |
lxml XML解析器 | (需要额外导入)BeautifulSoup(html,[‘lxml’,’xml’])或BeautifulSoup(html,’xml’) | 唯一支持解析xml | 需要安装C语言库 |
htm5lib | BeautifulSoup(html,’htm5llib’) | 以浏览器方式解析,最好的容错性,生成html5 | 速度慢 |
三、bs4使用
1、bs4对html文件的处理
BeautifulSoup4将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种:
(1)Tag
(2)NavigableString
(3)BeautifulSoup
(4)Comment
(1)Tag : Tag通俗点讲就是HTML中的一个个标签
from bs4 import BeautifulSoup
soup = BeautifulSoup(resp.text, 'html.parser')
# 获取title标签的所有内容
print(soup.title)
# 获取head标签的所有内容
print(soup.head)
# 获取第一个a标签的所有内容
print(soup.a)
# 类型
print(type(soup.a))
我们可以利用 soup 加标签名轻松地获取这些标签的内容,这些对象的类型是 bs4.element.Tag
但是注意,它查找的是在所有内容中的第一个符合要求的标签。
对于 Tag,它有两个重要的属性,是 name 和 attrs:
# [document]
#bs 对象本身比较特殊,它的 name 即为 [document]
print(soup.name)
# head
#对于其他内部标签,输出的值便为标签本身的名称
print(soup.head.name)
# 在这里,我们把 a 标签的所有属性打印输出了出来,得到的类型是一个字典。
print(soup.a.attrs)
#还可以利用get方法,传入属性的名称,二者是等价的
print(soup.a['class']) # soup.a.get('class')
# 可以对这些属性和内容等等进行修改
soup.a['class'] = "newClass"
print(soup.a)
# 还可以对这个属性进行删除
del soup.a['class']
print(soup.a)
(2)NavigableString:获取标签内部的文字用 .string 即可
print(soup.title.string) # 也可以soup.title.text
print(type(soup.title.string))
(3)BeautifulSoup:表示一个文档的内容
大部分时候,可以把它当作 Tag 对象,是一个特殊的 Tag,我们可以分别获取它的类型,名称,以及属性
print(type(soup.name))
print(soup.name)
print(soup.attrs)
(4)Comment:是一个特殊类型的 NavigableString 对象,其输出的内容不包括注释符号
print(soup.a) # 此时不能出现空格和换行符,a标签如下:
# <a class="mnav" href="http://news.baidu.com" name="tj_trnews"><!--新闻--></a>
print(soup.a.string) # 新闻
print(type(soup.a.string)) # <class 'bs4.element.Comment'>
2、遍历文档树
(1).contents:获取Tag的所有子节点,返回一个list
# tag的.content 属性可以将tag的子节点以列表的方式输出
print(soup.head.contents)
# 用列表索引来获取它的某一个元素
print(soup.head.contents[1])
(2).children:获取Tag的所有子节点,返回一个生成器
for child in soup.body.children:
print(child)
3、搜索文档树
1、find_all(name, attrs, recursive, text, **kwargs)
(1)name参数:
字符串过滤:会查找与字符串完全匹配的内容
a_list = soup.find_all("a")
print(a_list)
正则表达式过滤:如果传入的是正则表达式,那么BeautifulSoup4会通过search()来匹配内容
t_list = soup.find_all(re.compile("a"))
for item in t_list:
print(item)
列表: 如果传入一个列表,BeautifulSoup4将会与列表中的任一元素匹配到的节点返回
t_list = soup.find_all(["meta","link"])
for item in t_list:
print(item)
方法: 传入一个方法,根据方法来匹配
def name_is_exists(tag):
return tag.has_attr("name")
t_list = soup.find_all(name_is_exists)
for item in t_list:
print(item)
(2)kwargs参数:
# 查询id=head的Tag
t_list = soup.find_all(id="head")
print(t_list)
# 查询href属性包含ss1.bdstatic.com的Tag
t_list = soup.find_all(href=re.compile("http://news.baidu.com"))
print(t_list)
# 查询所有包含class的Tag(注意:class在Python中属于关键字,所以加_以示区别)
t_list = soup.find_all(class_=True)
for item in t_list:
print(item)
(3)attrs参数:
并不是所有的属性都可以使用上面这种方式进行搜索,比如HTML的data-*属性,我们可以使用attrs参数,定义一个字典来搜索包含特殊属性的tag:
t_list = soup.find_all(attrs={"data-foo":"value"})
for item in t_list:
print(item)
(4)text参数:
通过text参数可以搜索文档中的字符串内容,与name参数的可选值一样。text参数接受 字符串,正则表达式,列表
t_list = soup.find_all(attrs={"data-foo": "value"})
for item in t_list:
print(item)
t_list = soup.find_all(text="hao123")
for item in t_list:
print(item)
t_list = soup.find_all(text=["hao123", "地图", "贴吧"])
for item in t_list:
print(item)
t_list = soup.find_all(text=re.compile("\d"))
for item in t_list:
print(item)
(5)limit参数:
传入一个limit参数来限制返回的数量,如下列放回数量为2
t_list = soup.find_all("a",limit=2)
for item in t_list:
print(item)
2、find()
返回符合条件的第一个Tag;即当我们要取一个值的时候就可以用这个方法
t = soup.div.div
# 等价于
t = soup.find("div").find("div")
4、CSS选择器
(1)通过标签名查找
print(soup.select('title'))
print(soup.select('a'))
(2)通过类名查找
print(soup.select('.mnav'))
(3)通过id查找
print(soup.select('#u1'))
(4)组合查找
print(soup.select('div .bri'))
(5)属性查找
print(soup.select('a[class="bri"]'))
print(soup.select('a[href="http://tieba.baidu.com"]'))
(6)获取内容
t_list = soup.select("title")
print(soup.select('title')[0].get_text())
示例代码:
from bs4 import BeautifulSoup
'''
bs4
安装:pip install beautifulsoup4
官网:https://beautifulsoup.readthedocs.io/zh-cn/v4.4.0/
'''
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>
"""
# 创建一个BeautifulSoup对象
soup = BeautifulSoup(html_doc)
print(f"soup.prettify:{soup.prettify}")
print(f"soup.title:{soup.title}") # soup.title:<title>The Dormouse's story</title>
print(f"type(soup.title):{type(soup.title)}") # type(soup.title):<class 'bs4.element.Tag'>
print(f"dir(soup.title):{dir(soup.title)}") # dir(soup.title):['EMPTY_ELEMENT_EVENT', 'END_ELEMENT_EVENT', 'MAIN_CONTENT_STRING_TYPES', 'START_ELEMENT_EVENT', 'STRING_ELEMENT_EVENT', '_TreeTraversalEvent', '__annotations__', '__bool__', '__call__', '__class__', '__contains__', '__copy__', '__deepcopy__', '__delattr__', '__delitem__', '__dict__', '__dir__', '__doc__', '__eq__', '__firstlineno__', '__format__', '__ge__', '__getattr__', '__getattribute__', '__getitem__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__static_attributes__', '__str__', '__subclasshook__', '__unicode__', '__weakref__', '_all_strings', '_event_stream', '_find_all', '_find_one', '_format_tag', '_indent_string', '_insert', '_is_xml', '_lastRecursiveChild', '_last_descendant', '_namespaces', '_self_and', '_should_pretty_print', 'append', 'attribute_value_list_class', 'attrs', 'can_be_empty_element', 'cdata_list_attributes', 'childGenerator', 'children', 'clear', 'contents', 'copy_self', 'css', 'decode', 'decode_contents', 'decompose', 'decomposed', 'default', 'descendants', 'encode', 'encode_contents', 'extend', 'extract', 'fetchAllPrevious', 'fetchNextSiblings', 'fetchParents', 'fetchPreviousSiblings', 'find', 'findAll', 'findAllNext', 'findAllPrevious', 'findChild', 'findChildren', 'findNext', 'findNextSibling', 'findNextSiblings', 'findParent', 'findParents', 'findPrevious', 'findPreviousSibling', 'findPreviousSiblings', 'find_all', 'find_all_next', 'find_all_previous', 'find_next', 'find_next_sibling', 'find_next_siblings', 'find_parent', 'find_parents', 'find_previous', 'find_previous_sibling', 'find_previous_siblings', 'format_string', 'formatter_for_name', 'get', 'getText', 'get_attribute_list', 'get_text', 'has_attr', 'has_key', 'hidden', 'index', 'insert', 'insert_after', 'insert_before', 'interesting_string_types', 'isSelfClosing', 'is_empty_element', 'known_xml', 'name', 'namespace', 'next', 'nextGenerator', 'nextSibling', 'nextSiblingGenerator', 'next_element', 'next_elements', 'next_sibling', 'next_siblings', 'parent', 'parentGenerator', 'parents', 'parserClass', 'parser_class', 'prefix', 'preserve_whitespace_tags', 'prettify', 'previous', 'previousGenerator', 'previousSibling', 'previousSiblingGenerator', 'previous_element', 'previous_elements', 'previous_sibling', 'previous_siblings', 'recursiveChildGenerator', 'renderContents', 'replaceWith', 'replaceWithChildren', 'replace_with', 'replace_with_children', 'select', 'select_one', 'self_and_descendants', 'self_and_next_elements', 'self_and_next_siblings', 'self_and_parents', 'self_and_previous_elements', 'self_and_previous_siblings', 'setup', 'smooth', 'sourceline', 'sourcepos', 'string', 'strings', 'stripped_strings', 'text', 'unwrap', 'wrap']
print(f"soup.title.text:{soup.title.text}") # soup.title.text:The Dormouse's story
# 取出第一个a标签的所有属性
print(f"取出第一个a标签的所有属性:{soup.a.attrs}") # {'href': 'http://example.com/elsie', 'class': ['sister'], 'id': 'link1'}
# 取出a标签的href属性
print(f"取出a标签的href属性:{soup.a.attrs['href']}") # 取出a标签的href属性:http://example.com/elsie
# 判断是否有class属性
print(f"判断是否有class属性:{soup.a.has_attr('class')}") # 判断是否有class属性:True
# 取出第一个p标签下的所有子节点
print(f"取出第一个p标签下的所有子节点:{soup.p.children}") # 取出第一个p标签下的所有子节点:<generator object Tag.children.<locals>.<genexpr> at 0x000001C1E376C1C0>
print(f"取出第一个p标签下的所有子节点使用list转换:{list(soup.p.children)}") # 取出第一个p标签下的所有子节点使用list转换:[<b>The Dormouse's story</b>]
print(f"取出第一个p标签下的所有子节点使用list转换:{list(soup.p.children)[0]}") # 取出第一个p标签下的所有子节点使用list转换:<b>The Dormouse's story</b>
print(f"取出第一个p标签下的所有子节点使用list转换:{list(soup.p.children)[0].text}") # 取出第一个p标签下的所有子节点使用list转换:The Dormouse's story
for child in soup.p.children:
print(f"child:{child}") # child:<b>The Dormouse's story</b>
# 取出页面下所有a标签的href属性链接
soup.find_all('a')
for a in soup.find_all('a'):
print(f"a.attrs['href']:{a.attrs['href']}")
# a.attrs['href']:http://example.com/elsie
# a.attrs['href']:http://example.com/lacie
# a.attrs['href']:http://example.com/tillie
# 取出id=link3的a标签
print(f"soup.find:{soup.find(id='link3')}") # soup.find:<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
print(f"soup.find:{soup.find(_class='story')}")
# 取出页面内所有文本
print(f"soup.get_text:{soup.get_text()}")
# soup.get_text:
# The Dormouse's story
#
# The Dormouse's story
# Once upon a time there were three little sisters; and their names were
# Elsie,
# Lacie and
# Tillie;
# and they lived at the bottom of a well.
# ...
# CSS选择器select
print(f"soup.select选择器:{soup.select('.story')}")
# [<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>]
print(f"soup.select选择器:{soup.select('#link1')}") # soup.select选择器:[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]
print(f"soup.select选择器:{soup.select('.story a')}") # soup.select选择器:[<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>]
print(f"soup.select选择器:{soup.select('p.story')}")