**BeautifulSoup 是一个可以从HTML或XML文件中提取数据的Python库,**它的使用方式相对于正则来说更加的简单方便,常常能够节省我们大量的时间。(cmd命令pipinstall beautifulsoup4即可)
官方中文文档的:https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html
BeautifulSoup解析网页需要指定一个可用的解析器,以下是主要几种解析器:
由于这个解析的过程在大规模的爬取中是会影响到整个爬虫系统的速度的,所以推荐使用的是lxml,速度会快很多,而lxml需要单独安装:
pip install lxml
soup = BeautifulSoup(html_doc, 'lxml') # 指定
提示:如果一段HTML或XML文档格式不正确的话,那么在不同的解析器中返回的结果可能是不一样的,所以要指定某一个解析器。
html_doc = """
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.
...
"""
from bs4 import BeautifulSoup
# 使用BeautifulSoup解析上述代码,能够得到一个 BeautifulSoup 的对象
soup = BeautifulSoup(html_doc,"lxml") #将html转化为可操作的对象
# print(type(soup)) #输出:
# print(soup.prettify()) #.prettify()按照标准的缩进格式的结构输出
########################第零类:入门
print(soup.title) #获取title标签
print(soup.title.name) #获取title标签的标签名
print(soup.title.string) #获取title标签的文本内容
print(soup.title.parent) #获取title标签的父标签
print(soup.find(id="link2")) #找到id=link2的标签
print("第一类:Tag对象","*"*50)
########################第一类:Tag对象
'''
tag对象可以说是BeautifulSoup中最为重要的对象,通过BeautifulSoup来提取数据基本都围绕着这个对象来进行操作。
'''
print("我是Tag对象:",type(soup.a)) #输出为:
#1.获取标签 仅获取第一个符合条件的标签
print(soup.a) #输出为:Elsie
#2.获取属性 仅获取第一个符合条件的标签的属性值
print(soup.a["href"]) #输出为:http://example.com/elsie
#3.获取文本内容 有两种方法:
# 第一个例子:
print(soup.a.text) #输出都为:Elsie
print(soup.a.get_text())
#第二个例子:
print(soup.body.get_text()) #发现:这个方法会获取选中标签下的所有文本内容
# 简要讲一下NavigableString对象:
'''
NavigableString的意思是可以遍历的字符串,一般被标签包裹在其中的的文本就是NavigableString格式。
'''
print(type(soup.body.get_text())) #输出为:
print(type(soup.p.string)) #输出为:
print("第二类:","*"*50)
##########################第二类
body=soup.body #获取整个body标签
#1.子代标签的选择
'''
contents和children:
通过contents可以获取某个节点所有的子节点,包括里面的NavigableString对象。获取的子节点是列表格式。
而通过children同样的是获取某个节点的所有子节点,但是返回的是一个迭代器,这种方式会比列表格式更加的节省内存。
'''
print(body.contents)
tags = body.children #打印body.children可知这是个迭代器。获取的是子代标签。
# print(list(tags))
# for tag in tags:
# print(tag)
#2.后代标签的选择
'''
descendants
contents和children获取的是某个节点的直接子节点,而无法获得子孙节点。
通过descendants可以获得所有子孙节点,返回的结果跟children一样,需要迭代或者转类型使用。
'''
tags_des=body.descendants #打印body.descendants可知这是个生成器。获取的是后代标签
# print(list(tags_des))
#3.兄弟标签的选择
'''
兄弟节点:
兄弟节点指的就是父节点相同的节点。 如果选中标签在body子节点的第一个位置,那么它没有上一级标签;
next_sibling 和 previous_sibling 同理,如果选中标签在body子节点的最后一个位置,那么它没有下一级标签。
next_siblings 和 previous_siblings
加s是获取上级或者下级所有的兄弟节点
'''
p = body.p #可以一层层获取指定标签,但是也只可以获取第一个符合条件的标签
# print(p)
print(p.next_sibling.next_sibling) #如果想获取符合条件的第二个标签,使用.next_sibling方法
print(body.previous_sibling.previous_sibling) #如果想获取符合条件的同级标签的上一个标签,使用.previous_sibling方法
#4.父级标签的选择
'''
父节点parent和parents
有时我们也需要去获取某个节点的父节点,也就是包裹着当前节点的节点
而使用parents则可以获得当前节点递归到顶层的所有父辈元素。
'''
print(p.parent) #.parent只能获取选中标签的父亲
p_parents = p.parents #.parents可以获取选中标签的所有的父辈元素
# print(p_parents) #打印可知这是个生成器
print(list(p_parents))
#4.string和strings
'''
string和strings:
我们常常会遇到需要获取某个节点中的文本值的情况,如果这个节点中只有一个字符串,那么使用string可以正常将其取出。
而如果这个节点中有多个字符串的时候,BeautifulSoup就无法确定要取出哪个字符串了,这时候需要使用strings。
使用stripped_strings可以将全是空白的行去掉。
'''
print(p.string) #输出为:The Dormouse's story
gg=body.strings #返回的是一个generator(生成器)
print(list(gg))
print(list(body.stripped_strings)) #会发现这个返回的相比上面直接用strings的区别:没有了空白行
print("第三类:find_all","*"*50)
########################第三类:find_all()
# 上方这些直接通过属性来进行访问属性的方法,很多时候只能适用于比较简单的一些场景,
# 所以BeautifulSoup还提供了搜索整个文档树的方法find_all()。
'''
源码:
def find_all(self, name=None, attrs={}, recursive=True, text=None)
功能:获取所有元素
返回值:一个可迭代对象(列表)
'''
#注意:find_all获取到的内容在列表里哦!!!
# 1.通过name搜索,find_all('p')可以直接查找出整个文档树中所有的p标签,并返回列表
print(soup.find_all("p"))
# 拓展:
print(soup.find_all(["p","a"])) #获取所有的p标签和a标签
#2.通过属性搜索,这时候我们可以通过传递给attrs一个字典参数来搜索属性。soup.find_all(attrs={'class': 'sister'})
print(soup.find_all(name="p",attrs={"class":"story"})) #获取p标签里class为story的p标签
#3.通过文本内容搜索,soup.find_all(text="Elsie")
print(soup.find_all(text="Elsie")) #输出为:['Elsie']
print(soup.find_all("a",text="Elsie")) #获取a标签里文本为Elsie的a标签/soup.find_all(text="Elsie")[0].parent也可以
# 小拓展:获取属性值
print(soup.find_all("a",text="Elsie")[0]["href"]) #获取满足条件的a标签的href
# 4.限制查找范围为子节点
# find_all()方法会默认的去所有的子孙节点中搜索,
# 而如果将recursive参数设置为False,则可以将搜索范围限制在直接子节点中。
print(soup.html.find_all("body",recursive=False)) #recursive为True时可以递归拿到后代元素;
# 为False时不可以递归只拿到子代元素。
#例如:此处:html标签下的body不管为True还是False都可以拿到,因为body是html的子代元素也是后代元素;
#但是如果此处是a标签,为False就拿不到东西,因为a标签是html标签的后代元素,而非子元素。
#5.结合使用正则表达式进行查找
'''
通过正则表达式来筛选查找结果在BeautifulSoup中,也是可以与re模块进行相互配合的,
将re.compile编译的对象传入find_all()方法,即可通过正则来进行搜索。
tags = soup.find_all(re.compile("^b"))
'''
import re
tags = soup.find_all(re.compile("^b")) #查找以b开头的标签(b标签和body标签)
print("第四类:css选择器","*"*50)
#######################第四类:css选择器
#1.css选择器
print(soup.select("p")) #获取所有p标签
print(soup.select("p>a")) #获取p标签下面的a标签 在列表里
XPath 是一门在 XML 文档中查找信息的语言。XPath 可用来在 XML 文档中对元素和属性进行遍历。
相比于BeautifulSoup,Xpath在提取数据时会更有效率。
在python中很多库都提供XPath的功能,但是最流行的还是lxml这个库,效率最高。(直接pip install lxml 即可)
html_doc = """
The Dormouse's story
The Dormouse's story
Once upon a time there were three little sisters; and their names were
Elsie,
999 and
Tillie;
and they lived at the bottom of a well.
...
"""
from lxml import etree #原理和beautiful一样,都是将html字符串转换为我们易于处理的标签对象
page = etree.HTML(html_doc) #返回了html节点
print(type(page)) #输出为:
######################第一部分:常用!
'''
XPath 使用路径表达式在 XML/HTML 文档中选取节点。节点是通过沿着路径或者 step 来选取的。
下面列出了最有用的路径表达式:
'''
# 1. 根据nodename(标签名字)选取标签的时候,只会选择子标签;比如:如果是儿子的儿子则选取不到。
print(page.xpath("body"))
#2. /从根节点选取 一级一级筛选(不能跳)
gen = page.xpath("/html")
print(gen)
#3. 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。 注意:是所有符合条件的
a = page.xpath("//a")
print(a)
#4. .选取当前标签
p = page.xpath("//p")[0] #先选择p标签的第一个
print(p.xpath("."))
print(p.xpath("./b")) #选取当前标签下的b标签
#5. ..选取当前标签的父节点
a = page.xpath("//a")[0]
print(a.xpath(".."))
# 6.获取标签的属性值
bb = page.xpath('//p[@class="story"]/@id') #获取标签的id属性值
print(bb)
print("第二部分:谓语","*"*50)
#########################第二部分:谓语
'''
谓语:
谓语用来查找某个或某些特定的节点或者包含某个指定值的节点
谓语被嵌在方括号中。实例:
下面列出了带有谓语的一些路径表达式,以及表达式的结果。
'''
#1.选取所有拥有属性class的p标签
j = page.xpath('//p[@class]')
print("j:",j)
#选取所有p标签,且拥有属性class同时值为story的标签
b = page.xpath('//p[@class="story"]')
print("b",b)
#2.选取所有的p标签,且其中a标签的文本值大于889。
print(page.xpath('//p[a>889]'))
#3.选取属于class为story的p标签 子元素的第一个a元素。
dd = page.xpath('//p[@class="story"]/a[1]') #如果是在xpath里进行索引选择,是从1开始
ee = page.xpath('//p[@class="story"]/a')[0] #如果是从列表里进行索引选择,是从0开始
print("dd:",dd)
print("ee",ee)
# 选取属于class为story的p标签 子元素的最后一个a元素。
ss = page.xpath('//p[@class="story"]/a[last()]')
print("ss:",ss)
# 选取属于class为story的p标签 子元素的倒数第二个a元素。
rr = page.xpath('//p[@class="story"]/a[last()-1]')
print("rr",rr)
# 选取最前面的两个属于class为story的p标签的子元素的a元素。
gg = page.xpath('//p[@class="story"]/a[position()<3]')
print("gg:",gg)
print("第三部分:获取文本","*"*50)
########################第三部分:获取文本
#1.用text()获取某个节点下的文本
contents=page.xpath("//p/a/text()") #获取文本数据 放在列表里
print(contents)
#2.用string()获取某个节点下所有的文本
con = page.xpath("string(//p)") #只拿到第一个标签下的所有文本
print(con)
print("第四部分:XPath通配符","*"*50)
########################第四部分:XPath通配符
'''
选取未知节点
XPath通配符可用来选取未知节点
'''
# 1.* 匹配任何元素节点
s = page.xpath("//p/*") #选择p标签的所有子元素
print(s)
#2.@* 匹配任何属性节点
ss = page.xpath("//p/@*") #选取选中标签(所有p标签)的所有的属性值
print(ss)
print("第五部分;使用|运算符","*"*50)
########################第五部分:使用|运算符
'''
选取多个路径
通过在路径表达式中使用"|"运算符,您可以选取若干个路径。
在下面,我们列出了一些路径表达式,以及这些表达式的结果:
'''
# 选取p元素的所有a和b元素
print(page.xpath('//p/a|//p/b'))
#选取文档中的所有a和b元素
print(page.xpath('//a|//b'))