# coding=utf-8
from bs4 import BeautifulSoup
import re
html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p id="my p" class="title">hello
<b id="bbb" class="boldest">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')
# ---------------------------------------------------遍历文档树-----------------------------------------------------------
# 1.用法
print(soup.p) #存在多个相同的标签则只返回第一个
print(soup.a) #存在多个相同的标签则只返回第一个
# 2.获取标签的名称
print(soup.p.name)
# 3.获取标签的属性
print(soup.p.attrs) #返回字典形式
attr=soup.p.attrs # 修改属性
print(attr)
attr['id']='ip'
print(attr)
# 4.获取标签的内容
print(soup.p.string) # p下的文本只有一个时,取到,否则为None
print(soup.p.strings) # 拿到一个生成器对象, 取到p下所有的文本内容
for line in soup.p.strings:
print(line) # 会输出换行\n,用stripped_strings可以去掉
# 5.嵌套选择
print(soup.head.title.string)
print(soup.body.a.string)
#6、子节点、子孙节点
print(soup.p.contents) #p下所有子节点,包括字符串和换行\n
print(soup.p.children) #得到一个迭代器,包含p下所有子节点
for i,child in enumerate(soup.p.children):
print(i,child,type(child)) #得到的字符串和标签的类型是不一样的
print(soup.p.descendants) #获取子孙节点,p下所有的标签都会选择出来
for i,child in enumerate(soup.p.descendants):
print(i,child)
# 7.父节点、祖先节点
print(soup.a.parent) #获取a标签的父节点
print(soup.a.parents) #找到a标签所有的祖先节点,父亲的父亲,父亲的父亲的父亲...
# 8.兄弟节点
print('=====>')
print(soup.a.next_sibling) #下一个兄弟
print(soup.a.previous_sibling) #上一个兄弟
print(list(soup.a.next_siblings)) #下面的兄弟们=>生成器对象
print(soup.a.previous_siblings) #上面的兄弟们=>生成器对象
# --------------------------------------------------方法-------------------------------------------------------------
# 1.删除
# clear,将标签的所有子标签全部清空(保留标签名)
soup.p.clear()
# decompose,递归的删除所有的标签
soup.p.descendants()
# extract,递归的删除所有的标签,并获取删除的标签
print(soup.p.extract())
# 2.decode,转换为字符串(含当前标签);decode_contents(不含当前标签)
soup.body.decode()
soup.body.decode_contents()
# 3.encode,转换为字节(含当前标签);encode_contents(不含当前标签)
soup.p.encode()
soup.p.encode_contents()
# 4.has_attr,检查标签是否具有该属性
soup.p.has_attr('id')
# 5.get_text,获取标签内部文本内容
soup.a.get_text()
# 5.index,检查标签在某标签中的索引位置
soup.body.index(soup.body.a)
# 6. is_empty_element,是否是空标签(是否可以是空)或者自闭合标签,
# 判断是否是如下标签:'br' , 'hr', 'input', 'img', 'meta','spacer', 'link', 'frame', 'base'
soup.br.is_empty_element()
# --------------------------------------五种过滤器-------------------------------------------------------
import re
print(soup.find_all(re.compile('^b'))) # 1.匹配所有以b开头的标签,返回列表
print(soup.find('a')) # 找到第一个a标签,如果没有返回none
print(soup.find_all('a')) # 2.返回列表,里面的元素为所有的a标签
print(soup.find_all(['a','p'])) # 3.匹配所有的a标签和p标签
print(soup.find_all(True)) # 4.True:可以匹配任何值,下面代码查找到所有的tag,但是不会返回字符串节点
for tag in soup.find_all(True):
print(tag.name)
# 5.方法:如果没有合适过滤器,那么还可以定义一个方法,方法只接受一个元素参数 ,如果这个方法返回 True 表示当前元素匹配并且被找到,如果不是则反回 False
def has_class_not_has_id(tag):
return tag.has_attr('class') and not tag.has_attr('id')
print(soup.find_all(has_class_not_has_id))
# ----------------------------------find_all( name , attrs , recursive , text , **kwargs )_______________________________
# 1.name: 搜索name参数的值可以使任一类型的 过滤器 ,字符窜,正则表达式,列表,方法或是 True .
soup.find_all(name=re.compile('^t'))
# 2.keyword: key=value的形式,value可以是过滤器:字符串 , 正则表达式 , 列表, True .
soup.find_all(id=re.compile('my'))
soup.find_all(href=re.compile('lacie'),id=re.compile('\d')) # 类要用class_
soup.find_all(id=True) # 查找有id属性的标签
# 有些tag属性在搜索不能使用,比如HTML5中的 data-* 属性:
data_soup = BeautifulSoup('<div data-foo="value">foo!</div>','lxml')
# data_soup.find_all(data-foo="value") #报错:SyntaxError: keyword can't be an expression
# 但是可以通过 find_all() 方法的 attrs 参数定义一个字典参数来搜索包含特殊属性的tag:
data_soup.find_all(attrs={"data-foo": "value"})
# [<div data-foo="value">foo!</div>]
# 3.按照类名查找,注意关键字是class_,class_=value,value可以是五种选择器之一
soup.find_all('a',class_='sister')
soup.find_all(class_=['sister','sss']) # 查找类为sister和sss的a标签,顺序错误也匹配不成功
soup.find_all(class_=re.compile('^sis'))
# 4.attrs
soup.find_all('p',attrs={'class':'story'})
# 5.text: 值可以是:字符,列表,True,正则
soup.find_all('a',text='Elsie')
# 6.limit参数:如果文档树很大那么搜索会很慢.如果我们不需要全部结果,可以使用 limit 参数限制返回结果的数量.
# 效果与SQL中的limit关键字类似,当搜索到的结果数量达到 limit 的限制时,就停止搜索返回结果
soup.find_all('a',limit=2)
# 7.recursive:调用tag的 find_all() 方法时,Beautiful Soup会检索当前tag的所有子孙节点,如果只想搜索tag的直接子节点,可以使用参数 recursive=False .
soup.html.find_all('a')
soup.html.find_all('a',recursive=False)
# soup.find_all('a')可以简写为
soup('a')
# soup.title.find_all(text=True)可以简写为
soup.title(text=True)
# -------------------------find( name , attrs , recursive , text , **kwargs )-------------------------------------------
# find的用法与find_all一样,只是find_all返回的是一个空列表,而find返回的是一个tag,不存在时返回none
# soup.head.title 是 tag的名字 方法的简写.这个简写的原理就是多次调用当前tag的 find() 方法:
soup.head.title
# <title>The Dormouse's story</title>
soup.find("head").find("title")
# <title>The Dormouse's story</title>