对象的种类:Beautiful Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种: Tag
、NavigableString
、BeautifulSoup
、Comment
。
1 Tag:Tag
对象与XML或HTML原生文档中的tag相同
属性 | 解释 | 属性 | 解释 |
.name | 获取tag的名字 | .attrs | 获取tag的属性,返回一个字典 |
多值属性:返回类型为List。如果某个属性看起来好像有多个值,但在任何版本的HTML定义中都没有被定义为多值属性,那么Beautiful Soup会将这个属性作为字符串返回。
2 NavigableString:字符串常被包含在tag内.Beautiful Soup用 NavigableString
类来包装tag中的字符串, NavigableString
字符串与Python中的Unicode字符串相同。该类包含CData
, ProcessingInstruction
, Declaration
, Doctype
,Comment子类.
方法 | 解释 | 方法 | 解释 |
unicode() | 将 NavigableString 对象转换成Unicode字符串 | replace_with() | 替换字符串,tag中包含的字符串不能编辑,但是可以被替换成其它的字符串 |
3 BeautifulSoup:BeautifulSoup
对象表示的是一个文档的全部内容,大部分时候,可以把它当作 Tag
对象。由于 BeautifulSoup
对象并不是真正的HTML或XML的tag,所以它没有name和attribute属性,但 BeautifulSoup
对象包含了一个值为 “[document]” 的特殊属性 .name
4 Comment:Comment
对象是一个特殊类型的 NavigableString
对象,Comment类型的字符串出现在HTML文档中会准换位注释。
BeautifulSoup属性及方法:
属性 | 解释 | 属性 | 解释 |
.tagname | 获取<tagname>标签的内容 | .contents | 将tag的直接子节点以列表的方式输出,字符串没有.contents属性,因为字符串没有子节点 |
.children | 获取tag的直接子节点对象 | .descendants | 获取tag的所有子孙节点对象 |
.string | 当前tag只有一个 NavigableString 类型子节点,或者仅有一个子节点时可以使用 .string 得到子节点,如果tag包含多个子节点,tag就无法确定 .string 方法应该调用哪个子节点的内容, .string 的输出结果是 None | .strings | 如果tag中包含多个字符串 ,可以使用 .strings 来循环获取 |
.stripped_strings | 去除字符串中多余空白内容,全部是空格的行会被忽略掉,段首和段末的空白会被删除 | .parent | 获取某个元素的父节点,文档的顶层节点比如<html>的父节点是 BeautifulSoup 对象,BeautifulSoup 对象的 .parent 是None |
.parents | 获取tag的所有父辈节点对象 | .next_sibling | 当前节点的后一个兄弟节点(包含字符串和空白字符) |
.previous_sibling | 当前节点的前一个兄弟节点(包含字符串和空白字符) | .next_siblings | 当前节点之后的所有兄弟节点的对象(包含字符串和空白字符) |
.previous_siblings | 当前节点之前的所有兄弟节点的对象(包含字符串和空白字符) | .next_element | 指向解析过程中下一个被解析的对象(字符串或tag),结果可能与 .next_sibling 相同,但通常是不一样的 |
.previous_element | 与 .next_element 相反,它指向当前被解析的对象的前一个解析对象 |
介绍BeautifulSoup搜索方法之前先介绍一下过滤器类型(字符串、正则表达式、列表、方法、True),这些过滤器贯穿整个搜索的API.过滤器可以被用在tag的name中,节点的属性中,字符串中或他们的混合中:
- 字符串:查找与字符串完整匹配的内容,如果传入字节码参数,Beautiful Soup会当作UTF-8编码,可以传入一段Unicode 编码来避免Beautiful Soup解析编码出错
soup.find_all('b')
# [<b>The Dormouse's story</b>]
- 正则表达式:通过正则表达式的
match()
来匹配内容.
import re
for tag in soup.find_all(re.compile("^b")):
print(tag.name)
# body
# b
- 列表:将与列表中任一元素匹配的内容返回.
soup.find_all(["a", "b"])
# [<b>The Dormouse's story</b>,
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
- 方法:方法只接受一个元素参数,如果这个方法返回
True
表示当前元素匹配并且被找到,如果不是则反回False
def has_class_but_no_id(tag):
return tag.has_attr('class') and not tag.has_attr('id')
soup.find_all(has_class_but_no_id)
# [<p class="title"><b>The Dormouse's story</b></p>,
# <p class="story">...</p>]
- True:可以匹配任何值,下面代码查找到所有的tag,但是不会返回字符串节点
for tag in soup.find_all(True):
print(tag.name)
# html
# head
# title
# body
# b
# p
# a
# a
方法 | 解释 | 方法 | 解释 |
.find(name,attrs,recursive,string,**kwargs) | 搜索当前节点的所有子节点,并判断是否符合过滤器的条件.返回一个结果 | .find_all(name,attrs,recursive, string,**kwargs) | 搜索当前节点的所有子节点,并判断是否符合过滤器的条件.返回一个列表 |
.find_parent(name,attrs,recursive,string, **kwargs) | 搜索当前节点的所有父辈节点,并判断是否符合过滤器的条件.返回一个结果 | .find_parents(name,attrs, recursive,string,**kwargs) | 搜索当前节点的所有父辈节点,并判断是否符合过滤器的条件.返回一个列表 |
.find_previous_sibling(name,attrs,recursive, string,**kwargs) | 返回第一个符合条件的前面的兄弟节点 | .find_previous_siblings(name, attrs,recursive,string,**kwargs) | 返回所有符合条件的前面的兄弟节点 |
.find_next_sibling(name,attrs,recursive, string,**kwargs) | 返回第一个符合条件的后面的兄弟节点 | .find_next_siblings(name,attrs, recursive,string,**kwargs) | 返回所有符合条件的后面的兄弟节点 |
.find_previous(name,attrs,recursive,string, **kwargs) | 通过.previous_elements 属性对当前节点前面的tag和字符串进行迭代, 返回第一个符合条件的节点 | .find_all_previous(name,attrs, recursive,string,**kwargs) | 通过.previous_elements 属性对当前节点前面的tag和字符串进行迭代, 返回所有符合条件的节点 |
.select() | 在 Tag 或 BeautifulSoup 对象的 .select() 方法中传入字符串参数, 即可使用CSS选择器的语法找到tag |
具体介绍一下上表方法的各种参数(name,attrs,recursive,string,**kwargs):
- name:查找所有名字为
name
的tag,字符串对象会被自动忽略掉. [接受任一类型过滤器] - attrs:按css搜索,通过class_参数搜索有指定css类名的tag。(
class
在Python中是保留字,使用class
做参数会导致语法错误.)tag的class
属性是 多值属性 .按照CSS类名搜索tag时,可以分别搜索tag中的每个CSS类名,也可以通过css值完全匹配. [接受字符串、正则表达式、方法、True] - string:搜索文档中的字符串内容. [接受字符串、正则表达式、列表、True]
- recursive:只查找直接子节点
- **kwargs
- limit:限制返回结果的数量.效果与SQL中的limit关键字类似,当搜索到的结果数量达到
limit
的限制时,就停止搜索返回结果. - keyword:如果一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当作指定名字tag的属性来搜索. [接受字符串、正则表达式、列表、True]
方法 | 解释 | 方法 | 解释 |
.append() | 向tag中追加内容 | .NavigableString() | NavigableString 的构造方法,向文档中添加文本内容、注释或是NavigableString 的任何子类。 |
.new_tag() | 创建一个tag,第一个参数作为tag的name,是必填,其它参数选填 | .insert() | 将元素插入到指定位置 |
.insert_before() | 在当前tag或文本节点前插入内容 | .insert_after() | 在当前tag或文本节点后插入内容 |
.clear | 移除当前tag的内容 | .extract() | 将当前tag移除文档树,并作为方法结果返回,被移除并返回的tag可以继续调用 extract 方法 |
.decompose() | 将当前节点移除文档树并完全销毁 | .replace_with() | 移除文档树中的某段内容,并用新tag或文本节点替代它 |
.wrap() | 对指定的tag元素进行包装,并返回包装后的结果 | .unwrap() | 移除tag内的所有tag标签,并返回被移除的tag.该方法常被用来进行标记的解包 |
代码演示
from bs4 import BeautifulSoup
#bs4主要用于网页分析,在分析的时候主要是按照css选择器的规则来选择
#创建一个bs4对象
soup = BeautifulSoup(open('test.html',encoding='utf8'),'lxml')
#参数1,代表需要解析的HTML字符串(此处为test.html页面,代码太多不粘了,没有也看得懂);参数2,代表一个解析器(可选参数,传了以后可以增加bs4的解析速度)
print(soup) #html页面
#************1 根据标签名查找对象,返回的是这一类标签中的第一个对象*************
print(soup.title) #<title> </title>
print(soup.div) #html页面中的第一个div
print(soup.li) #html页面中第一个li
#*************2 获取节点的属性值*************************
obj = soup.a #html页面中的第一个a标签对象
# 用标签的形式
print(obj.get('href')) #obj中的href属性的值
# 用键值的形式
print(obj['href']) #obj中的href属性的值
# 用属性的形式
print(obj.attrs) #obj中的属性及值(是一个字典对象){'href': 'http://www.baidu.com'}
#获取标签的属性值
print(obj.name) #该标签的名字,此处是a
#*************3 获取标签中的内容***************************
# 1)使用string属性获取内容时,获取的是纯字符串,如果不是纯字符串则返回None,但是string属性可以获取注释内容
print(obj.string) #obj中的纯字符串内容
# 2)使用get_text()函数获取内容时,获取的是当前标签以及其子标签中的字符串内容,但是不包括注释内容
print(obj.get_text()) #obj中的纯字符串内容及其子字符串中的内容
#*************4 获取字标签********************************
# 1)获取直接子节点 得到的是一个迭代器对象,如果要获取迭代器中的内容,需要循环遍历
child = soup.body.children
print(child) #<list_iterator object at 0x000000000285BA58>
# for c in child:
# print('------------')
# print(c)
#2)获取所有的后代节点 得到的是一个生成器对象,如果要获取生成器中的内容,需要循环遍历
des = soup.body.descendants
print(des) #<generator object descendants at 0x0000000002BC56D0>
# for d in des:
# print('----------')
# print(d)
#**************5 根据相关函数来查找节点***********************
#1)find函数,返回一个对象
print(soup.find('li',id='emmmm')) #所有id为emmmm的li中的第一个li
print(soup.find('li',class_='2333')) #所有class属性为2333的li,class属性后面加下划线
#2)findall函数,返回所有对象
print(soup.find_all('li')) #所有li中的内容
#3)select函数,根据css选择器来查找
print(soup.select('.emmmm')) #类选择器
print(soup.select('div.emmmm')) #组合选择器 找到所有div,再从这些div里找id为emmmm的
#一组组合选择器中如果有标签选择器一定要把标签选择器放在最前面
print(soup.select('div .emmmm')) #派生选择器 先找到所有div,再从这些div的后代中找到id为emmmm的
print(soup.select('[id]')) #属性选择器 所有带id属性的