# 安装:pip install beautifulsoup4
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"><!-- 注释部分。。。。。--></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>
"""
#1、BeautifulSoup对象,通过构造方法,获取soup,传入一段字符串或一个文件句柄,第二个参数为html解析器
# soup = BeautifulSoup(open('index.html'),'html.parser')
soup = BeautifulSoup(html_doc,'html.parser') #其实也可以把soup看成一个Tag,因为遍历方法相同
print(soup) #soup的__str__方法输出文档内容,和soup.getText,soup.get_text一样
#soup.text,soup.get_text() 输s出子孙标签的内容
# 2、Tag标签对象
a = soup.a #获取文档树下的第一个a标签,其实内部是多次调用了find方法
print(a) #直接输出的为a标签以及子孙标签内容
print(a.name) #获取标签名
# tag的属性操作方法与字典部分一样
print(a['href']) #通过 Tag[属性] 直接获取属性
print(a.attrs['href']) #先获取到属性字典,再获取属性
print(a.get('href')) #直接通过get方法获得属性
#tag中包含的字符串内容
print(a.string) #通过string属性获取标签中间内容,类型:NavigableString
#多值属性处理:返回列表(但是对于html中没有定义的多属性值返回字符串)
#给一个属性赋值多个值(列表),则标签属性为包含两个值以空格隔开的字符串
print(a['class']) #res:['sister']
#当然也可以直接给标签属性赋值
a['href'] = 'http://www.baidu.com'
print(a)
# 4、Comment 注释对象,是一个特殊的 NavigableString
print(a.string,type(a.string)) #res: 注释部分。。。。。 <class 'bs4.element.Comment'>;注意类型
# 5、获取子节点
#除了String节点不支持遍历和操作子节点的属性和方法
print(soup.html) #直接 节点.标签名 获取该字标签;##不过只能获取第一个子标签
print(soup.head)
print(soup.a)
print(soup.head.title)
#6、获取子节点
print(soup.body.contents) #返回直y接子节点列表,包括文本节点(还有换行要注意点)
print(soup.body.children) #返回生成器,可循环遍历输出子节点
# eg:
print([i for i in soup.body.children])
for i in soup.body.children:
print(i,end='')
#7、获取子孙节点
soup.descendants #返回标签的子孙节点,返回一个迭代器,也包括换行等字符串内容
# eg:
print('======================')
print([i for i in soup.descendants])
for i in soup.descendants:
print(i,end='')
#8、获取标签中内容
print(soup.a.string) #获取字符串,其实本身是一个NavigableString对象,如果内容是注释那么是一个Comment对象,如果既有注释又有文本,返回None,如下:
print(soup.body.string) #如果tag包含了多个子节点,tag就无法确定,此时输出结果是 None
print(list(soup.strings)) #返回子孙点点的字符串,包括换行(多个连续换行为一个换行),此时Comment对象作为一个标签处理,不返回该内容
print(list(soup.stripped_strings)) #返回子节点字符串或子孙节点的字符串,去除了空格,空行或者换行(全部是空格的行会被忽略掉,段首和段末的空白会被删除)
#9、父节点 (字符串也有)
print(soup.a.string.parent) #返回该节点的所有父辈节点(字符串也有父辈节点)
print(soup.title) #soup父节点返回值为None
print('=================================')
print(tuple((i.name for i in soup.title.parents)))
#10、兄弟节点 .next_sibling 和 .previous_sibling
print(soup.head.next_sibling.next_sibling) #获取后一个兄弟节点,文本节点也算
print(soup.body.previous_sibling.previous_sibling) #获取前一个兄弟节点,文本节点也算
print(list(soup.find_all('a')[1].next_siblings))
print(list(soup.find_all('a')[1].previous_siblings))
#11、前一个节点 .previous_element,后一个节点 .next_element
#无论是否是兄弟还是子节点,反正就是输出该节点第一个标签名后的下一个或者上一个最近的标签
#获取多个
# previous_elements
# next_elements
#12、获取一个标签或者多个标签 tag.find() tag.find_all()
print(soup.find('a'))
print(soup.find_all('a'))
# 参数:
import re #正则---------获取正则匹配的标签名的标签
print(soup.find_all(re.compile('^b')))
#--列表 #获取列表中的所有标签
print(soup.find_all(['a','p']))
#True #查找到所有的tag,但是不会返回字符串节点
print('-------------------------------')
print([i.name for i in soup.find_all(True)])
#方法
# 定义一个方法,方法只接受一个元素参数 [4] ,如果这个方法返回 True 表示当前元素匹配并且被找到,如果不是则反回 False
def has_href(tag):
return tag.has_attr('href')
print(soup.find_all(has_href))
#定义方法,方法的参数是一个属性值,注意find_all的参数形式为:属性名=方法名
def not_lacie(href):
print(href and not re.compile("lacie").search(href))
return href and not re.compile("lacie").search(href)
soup.find_all(href=not_lacie)
#==============》学习find_all()
# find_all( name , attrs , recursive , string , **kwargs )
#-- name参数: 搜索 name 参数的值可以使任一类型的 过滤器 ,字符窜,正则表达式,列表,方法或是 True
print(soup.find_all('title'))
#keyword 参数: 搜索指定名字的属性对应的标签, 属性值可以是: 字符串 , 正则表达式 , 列表, True .
#当然多个指定名字的参数可以同时过滤tag的多个属性
soup.find_all(href=re.compile("elsie"), id='link1')
#attrs 参数:定义一个字典参数来搜索包含特殊属性的tag
soup.find_all(attrs={"data-foo": "value"})
#按照CSS查找,就是通过class_属性查找,相当于属性查找不过class可以通过本身多个对应class名查找
#class 在Python中是保留字,使用 class 做参数会导致语法错误.从Beautiful Soup的4.1.1版本开始,可以通过 class_ 参数搜索有指定CSS类名的tag:
soup.find_all("a", class_="sister")
soup.find_all("p", class_="body strikeout")
#string 参数: 通过 string 参数可以搜搜文档中的字符串内容.与 name 参数的可选值一样, string 参数接受 字符串 , 正则表达式 , 列表, True
print(soup.find_all(string="Lacie"))
# ['Elsie']
soup.find_all(string=["Tillie", "Elsie", "Lacie"])
# ['Elsie', 'Lacie', 'Tillie']
# **典型例子:
# 虽然 string 参数用于搜索字符串,还可以与其它参数混合使用来过滤tag.Beautiful Soup会找到 .string 方法与 string 参数值相符的tag.下面代码用来搜索内容里面包含“Elsie”的<a>标签:
soup.find_all("a", string="Elsie")
# [<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>]
#limit参数: 限制返回结果的数量,内部达到数量,停止搜索
soup.find_all("a", limit=2)
#recursive 参数: 是否检索当前tag的所有子孙节点,True:检索所有子孙节点,False:只检索直接子节点
print(soup.html.find_all("title", recursive=False))
print(soup.html.find_all("title", recursive=True))
#***与find_all等价写法:
soup.find_all('a')
soup('a')
soup.title.find_all(string=True)
soup.title(string=True)
# 13、CSS选择器 tag.select()
soup.select_one('a') #返回一个
soup.select("p>a:nth-of-type(3)") #返回列表
# [<p class="story">...</p>]
# 通过是否存在某个属性来查找:
soup.select('a[href]')
# [<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>]
#14、常用方法
# 格式化输出:prettify()
print(soup.prettify())
str(soup) #str() 方法返回UTF-8编码的字符串,可以指定 编码 的设置.