一.Beautiful Soup的基本概念
Beautiful Soup库是一个强大的基于Python语言的XML和HTML解析库,可以同它来方便的从网页中提取数据。
Beautiful Soup提供了一些简单的函数来处理导航、搜索、修改分析树等功能,他是一个工具箱,通过解析文档为用户提供需要抓取的数据。
Beautiful Soup自动将输入的文档转化为Unicode编码,输出文档转化为UTF-8编码,所以在使用的过程不需要考虑编码的问题,除非文档没有指定编码方式,这时只需要指定输入文档的编码方式即可。
二.安装Beautiful Soup库
Beautiful Soup的HTML和XML解析器依赖于lxml库,所以在安装Beautiful Soup库之前,确保本机安装了lxml库。
1.windows在线安装
pip install beautifulsoup4
2.离线安装
在网址中找到Beautiful Soup的whl文件,在cmd命令行中cd到该解压的文件夹中;执行如下命令
pip install beautifulsoup4‑4.9.3‑py3‑none‑any.whl
三.Beautiful Soup库的基本使用方法
3.1首先创建BeautifulSoup对象,BeautifulSoup类的第一个参数需要指定待分析的HTML代码,第二个参数指定lxml解析器(第二个参数加引号(''))。在解析html的过程中,BeautifulSoup对象会将HTML中的各个级别的标签映射成BeautifulSoup对象中同级别的属性,所以就可以直接通过BeautifulSoup对象的属性提取HTML代码中的内容。
from bs4 import BeautifulSoup
html = '''
<html>
<head><title>这是一个演示页面</title></head>
<body>
<a href='a.html'>第一页</a>
<p>
<a href='b.html'>第二页</a>
</body>
</html>
'''
# 创建BeautifulSoup对象,BeautifulSoup类的第一个参数需要指定待分析的HTML代码,第二个参数指定lxml解析器(第二个参数加引号(''))
soup = BeautifulSoup(html,'lxml')
# 获取<title>标签的文本
print('<' + soup.title.string + '>')
# 获取第一个<a>标签的href属性
print('[' + soup.a["href"]+ ']')
# 以格式化后的格式输出这段HTML代码
print(soup.prettify())
四.节点选择器
节点选择器通过节点的名称选取节点,然后再用string属性就可以得到节点内的文本,这种选择方式非常快。
4.1这里的知识点是:
获取节点的名称:name
获取节点的属性:attrs
获取节点的内容:string
from bs4 import BeautifulSoup
html = '''
<html>
<head>
<meta charset="UTF-8">
<title>Beautiful Soup演示</title>
</head>
<body>
<div>
<ul>
<li class="item1" value1="1234" value2 = "hello world"><a href="https://geekori.com"> geekori.com</a></li>
<li class="item2"><a href="https://www.jd.com"> 京东商城</a></li>
<li class="item3"><a href="https://www.taobao.com">淘宝</a></li>
<li class="item4" ><a href="https://www.microsoft.com">微软</a></li>
<li class="item5"><a href="https://www.google.com">谷歌</a></li>
</ul>
</div>
</body>
</html>
'''
soup = BeautifulSoup(html,'lxml')
# 获取节点tite的名称
print(soup.title.name)
# 获取第一个节点的所有属性名和属性值
print(soup.li.attrs)
# 获取第一个li节点的value2属性的值
print(soup.li.attrs["value2"])
print(soup.li["value1"])
# 获取第一个a节点的href属性的值
print(soup.a['href'])
# 获取第一个a标签的文本内容
print(soup.a.string)
4.2嵌套选择节点
通过BeautifulSoup对象的属性获得的每一个节点都是一个bs4.element.Tag对象。在该对象的基础上同样可以使用节点选择器进行下一步的选择。也就是继续选择Tag对应的节点的子节点,这也可以称为嵌套选择
from bs4 import BeautifulSoup
html = '''
<html>
<head>
<meta charset="UTF-8">
<title>Beautiful Soup演示</title>
</head>
<body>
<div>
<ul>
<li class="item1"><a href="https://www.jd.com"> 京东商城</a></li>
</ul>
</div>
</body>
</html>
'''
soup = BeautifulSoup(html,'lxml')
print(soup.head)
print(type(soup.head))
head = soup.head
print(head.title.string)
print(soup.body.div.ul.li.a['href'])
4.3选择子节点
1.直接获取子节点
通过contents和children数学,其中contents属性返回一个列表(list类的实例),children属性返回list_iterator类的实例,这是一个可迭代对象,该对象可以使用for循环进行迭代。总之,他们都返回一个列表(contents是列表对象,children是可迭代的对象,本质上是一个列表,只是不能直接输出)。每一个列表元素是一个字符串形成的子节点。
2.获取所有子孙节点
如果想要获取所有的子孙节点,需要使用descendants属性,该属性返回一个产生器(generator),需要使用for循环进行迭代才可以输出产生器的值。
from bs4 import BeautifulSoup
html = '''
<html>
<head>
<meta charset="UTF-8">
<title>Beautiful Soup演示</title>
<tag1><a><b></b></a></tag1>
</head>
<body>
<div>
<ul>
<li class="item1" value = "hello world">
<a href="https://geekori.com">
geekori.com
</a>
</li>
<li class="item2"><a href="https://www.jd.com"> 京东商城</a></li>
</ul>
</div>
</body>
</html>
'''
soup = BeautifulSoup(html,'lxml')
# 输出head的所有直接子节点
print(soup.head.contents)
print(soup.head.children)
print(type(soup.head.contents))
print(type(soup.body.div.ul.children))
print(type(soup.head.descendants))
# 对ul中的子节点进行迭代,并以文本形式输出子节点的内容
for i, child in enumerate(soup.body.div.ul.contents):
print(i,child)
print('----------')
i = 1
for child in soup.body.div.ul.children:
print('<', i, '>',child, end=" ")
i += 1
print('----------')
# 对ul中的子孙节点进行迭代,并以文本形式输出子节点的内容
for i, child in enumerate(soup.body.div.ul.descendants):
print('[',i,']',child)
4.4选择父节点
如果要选取某一个节点的直接父节点,需要使用parent属性,如果要选取某个节点的所有父节点,需要使用parents属性。parent属性返回当前节点的父节点的Tag对象,而parents属性会返回一个可迭代对象,通过for循环可以对该对象进行迭代,并获得当前节点所有的父节点对应的Tag对象。
from bs4 import BeautifulSoup
html = '''
<html>
<head>
<meta charset="UTF-8">
<title>Beautiful Soup演示</title>
<tag1><xyz><b></b></xyz></tag1>
</head>
<body>
<div>
<ul>
<li class="item1" value = "hello world">
<a href="https://geekori.com">
geekori.com
</a>
</li>
<li class="item2"><a href="https://www.jd.com"> 京东商城</a></li>
</ul>
</div>
</body>
</html>
'''
soup = BeautifulSoup(html,'lxml')
print(soup.a.parent)
print(soup.a.parent['class'])
print(soup.a.parents)
for parent in soup.a.parents:
print('<',parent.name,'>')
4.5选择兄弟节点
同级节点也称兄弟节点,可以通过next_sibling属性获得当前节点的下一个兄弟节点,通过previous_sibling属性获得当前节点的上一个兄弟节点。通过next_siblings属性获得当前节点的后面所有的个兄弟节点(返回一个可迭代对象),通过previous_sibling属性获得当前节点前面所有的兄弟节点(返回一个可迭代对象)。
注意:如果两个节点之间有换行符或其他文本,那么这些属性也同样会返回这些文本节点,节点之间的文本将作为一个文本节点处理。
from bs4 import BeautifulSoup
html = '''
<html>
<head>
<meta charset="UTF-8">
<title>Beautiful Soup演示</title>
</head>
<body>
<div>
<ul>
<li class="item1" value1="1234" value2 = "hello world">
<a href="https://geekori.com"> geekori.com</a>
</li>
<li class="item2"><a href="https://www.jd.com"> 京东商城</a></li>
<li class="item3"><a href="https://www.taobao.com">淘宝</a></li>
<li class="item4" ><a href="https://www.microsoft.com">微软</a></li>
<li class="item5"><a href="https://www.google.com">谷歌</a></li>
</ul>
</div>
</body>
</html>
'''
soup = BeautifulSoup(html,'lxml')
secondli = soup.li.next_sibling.next_sibling
print('第1个li节点的下一个li节点:',secondli)
print('第2个li节点的上一个li节点的class属性值:', secondli.previous_sibling.previous_sibling['class'])
for sibling in secondli.next_siblings:
print(type(sibling))
if str.strip(sibling.string) == "":
print('换行')
else:
print(sibling)
五.方法选择器
前面讲的方法都是通过属性来选择节点的,对于比较简单的选择,这种方法使用起来非常方便快捷,但对于比较复杂的选择,这种方法就显得比较笨拙,不够灵活。Beautiful Soup还提供了一些查询方法,如find_all、find等,调用这些方法,然后传入相应的参数,就可以灵活选择节点了。
5.1 find_all方法
find_all方法用于根据节点名、属性、文本内容等选择符合要求的节点。
find_all方法的原型如下:
def find_all(self,name=None,attrs={},recursive=True,text=None,limit=None,**kwargs):
常用的参数包括name、attrs和text,下面分别介绍这几个参数的用法
#5.1.1name参数
name参数用于指定节点名,find_all方法会选取所有节点名与name参数值相同的节点,find_all方法返回一个bs4.element.ResultSet对象,该对象是可迭代的,可以通过迭代
获取每一个符合条件的节点(Tag对象);find_all方法属于Tag对象,由于BeautifulSoup是Tag的子类,所以find_all方法在beautifulSoup对象上
也是可以调用的。因此,对ResultSet对象迭代未获得的每一个Tag对象仍然可以继续使用find_all方法以当前Tag对象的节点作为根开始继续选取节点
这种方式称为嵌套查询
本例通过find_all方法的name参数选择所有的ul节点,然后对每个ul节点继续使用find_all方法搜索当前ul节点下的所有li节点
from bs4 import BeautifulSoup
html = '''
<html>
<head>
<meta charset="UTF-8">
<title>Beautiful Soup演示</title>
</head>
<body>
<div>
<ul>
<li class="item1" value1="1234" value2 = "hello world"><a href="https://geekori.com"> geekori.com</a></li>
<li class="item2"><a href="https://www.jd.com"> 京东商城</a></li>
</ul>
<ul>
<li class="item3"><a href="https://www.taobao.com">淘宝</a></li>
<li class="item4" ><a href="https://www.microsoft.com">微软</a></li>
<li class="item5"><a href="https://www.google.com">谷歌</a></li>
</ul>
</div>
</body>
</html>
'''
soup = BeautifulSoup(html,'lxml')
ulTags = soup.find_all(name='ul')
print(type(ulTags))
for ulTag in ulTags:
print(ulTag)
print('---------------------')
for ulTag in ulTags:
liTags = ulTag.find_all(name='li')
for liTag in liTags:
print(liTag)
5.1.2attrs参数
除了可以根据节点名查询外,还可以使用attrs参数通过节点的属性查找。attrs参数是一个字典类型,key是节点属性名,value是节点属性值。下面是一个使用attrs参数的例子
soup.find_all(attrs={'id':'button1'}) # 选择所有id属性值为button1的节点
如果是常用的属性,如,id和class,可以直接将这些属性作为命名参数传入find_all方法,而不必使用attrs参数。
在指定class属性值时,由于class是Python的关键字,所以在为find_all方法指定class参数时,需要在class后面加一个下画线(),也就是指定"class"参数
from bs4 import BeautifulSoup
html = '''
<div>
<ul>
<li class="item1" value1="1234" value2 = "hello world"><a href="https://geekori.com"> geekori.com</a></li>
<li class="item"><a href="https://www.jd.com"> 京东商城</a></li>
</ul>
<button id="button1">确定</button>
<ul>
<li class="item3"><a href="https://www.taobao.com">淘宝</a></li>
<li class="item" ><a href="https://www.microsoft.com">微软</a></li>
<li class="item2"><a href="https://www.google.com">谷歌</a></li>
</ul>
</div>
'''
soup = BeautifulSoup(html,'lxml')
tags = soup.find_all(attrs={"class":"item"})
for tag in tags:
print(tag)
tags = soup.find_all(class_='item2')
print(tags)
tags = soup.find_all(id='button1')
print(tags)
5.1.3text参数
通过text参数可以搜索匹配的文本节点,传入的参数可以是字符串,也可以是正则表达式对象
from bs4 import BeautifulSoup
import re
html = '''
<div>
<xyz>Hello World, what's this?</xyz>
<button>Hello, my button. </button>
<a href='https://geekori.com'>geekori.com</a>
</div>
'''
soup = BeautifulSoup(html,'lxml')
tags = soup.find_all(text='geekori.com')
print(tags)
tags = soup.find_all(text=re.compile('Hello'))
print(tags)
5.2find方法
find方法与find_all方法有如下几点不同
1.find方法用于查询满足条件的第1个节点,而find_all方法用于查询所有满足条件的节点
2.find_all方法返回bs4.element.ResultSet对象,而find方法返回的是bs4.element.Tag对象。
find方法和find_all方法的参数和使用方法完全相同
本例同时使用find_all方法和find方法根据相同的查询条件查询节点,并输出各自的查询结果
from bs4 import BeautifulSoup
html = '''
<div>
<ul>
<li class="item1" value1="1234" value2 = "hello world">
<a href="https://geekori.com"> geekori.com</a>
</li>
<li class="item"><a href="https://www.jd.com"> 京东商城</a></li>
</ul>
<ul>
<li class="item3"><a href="https://www.taobao.com">淘宝</a></li>
<li class="item" ><a href="https://www.microsoft.com">微软</a></li>
<li class="item2"><a href="https://www.google.com">谷歌</a></li>
</ul>
</div>
'''
soup = BeautifulSoup(html,'lxml')
tags = soup.find(attrs={"class":"item"})
print(type(tags))
print(tags)
print('----------------')
tags = soup.find_all(attrs={"class":"item"})
print(type(tags))
for tag in tags:
print(tag)
六.CSS选择器(select()方法在我的环境下失效)
6.1基本用法
使用CSS选择器需要使用Tag对象的select方法,该方法接收一个字符串类型的CSS选择器。常用的CSS选择器有如下几个:
(1).classname:选取样式值为classname的节点,也就是class属性值是classname的节点
(2)nodename:选取节点名为nodename的节点
(3)#idname:选取id属性值为idname的节点
本例使用CSS选择器根据class属性、节点名称和id属性查询特定的节点
from bs4 import BeautifulSoup
html = '''
<div>
<ul>
<li class="item1" value1="1234" value2 = "hello world"><a href="https://geekori.com"> geekori.com</a></li>
<li class="item"><a href="https://www.jd.com"> 京东商城</a></li>
</ul>
<button id="button1">确定</button>
<ul>
<li class="item3"><a href="https://www.taobao.com">淘宝</a></li>
<li class="item" ><a href="https://www.microsoft.com">微软</a></li>
<li class="item2"><a href="https://www.google.com">谷歌</a></li>
</ul>
</div>
'''
soup = BeautifulSoup(html,'lxml')
tags = soup.select('.item')
for tag in tags:
print(tag)
tags = soup.select('#button1')
print(tags)
tags = soup.select('a')[2:]
for tag in tags:
print(tag)
6.2嵌套选择器
CSS选择器与节点选择器一样,同样可以嵌套调用。也就是在通过CSS选择器选取一些节点后,可以在这些节点的基础上继续使用CSS选择器。
本例将CSS选择器与方法选择器混合使用来选取特定的节点
from bs4 import BeautifulSoup
html = '''
<div>
<ul>
<li class="item1" value1="1234" value2 = "hello world"><a href="https://geekori.com"> geekori.com</a></li>
<li class="item">
<a href="https://www.jd.com"> 京东商城</a>
<a href="https://www.google.com">谷歌</a>
</li>
</ul>
<ul>
<li class="item3"><a href="https://www.taobao.com">淘宝</a></li>
<li class="item" ><a href="https://www.microsoft.com">微软</a></li>
</ul>
</div>
'''
soup = BeautifulSoup(html,'lxml')
tags = soup.select('.item')
print(type(tags))
for tag in tags:
aTags = tag.select('a')
for aTag in aTags:
print(aTag)
print('---------')
for tag in tags:
aTags = tag.find_all(name='a')
for aTag in aTags:
print(aTag)
6.3获取属性值和文本
由于select方法同样会返回Tag对象的集合,所以可以使用Tag对象的方式获取节点属性值和文本内容。获取属性值可以使用attrs,也可以直接使用[...]方式引用节点的属性。获取节点的文本内容可以使用get_text方法,也可以使用string属性
本例使用CSS选择器选取特定的a节点,并获取a节点的href属性值和文本内容。
from bs4 import BeautifulSoup
html = '''
<div>
<ul>
<li class="item1" value1="1234" value2 = "hello world">
<a href="https://geekori.com"> geekori.com</a>
</li>
<li class="item">
<a href="https://www.jd.com"> 京东商城</a>
<a href="https://www.google.com">谷歌</a>
</li>
</ul>
<ul>
<li class="item3"><a href="https://www.taobao.com">淘宝</a></li>
<li class="item" ><a href="https://www.microsoft.com">微软</a></li>
</ul>
</div>
'''
soup = BeautifulSoup(html,'lxml')
tags = soup.select('.item')
print(type(tags))
for tag in tags:
aTags = tag.select('a')
for aTag in aTags:
print(aTag['href'],aTag.get_text())
print('---------')
for tag in tags:
aTags = tag.find_all(name='a')
for aTag in aTags:
print(aTag.attrs['href'],aTag.string)
6.4通过浏览器获取CSS选择器代码
在网页上,单击右键菜单中的"检查"命令,会显示开发者工具,在Elements选项卡中定位到要获取CSS选择器代码的节点。
本例使用requests库抓取京东商城首页的HTML代码,并使用CSS选择器获取导航条的链接文本。
import requests
from bs4 import BeautifulSoup
result = requests.get('https://www.jd.com')
soup = BeautifulSoup(result.text,'lxml')
aTag = soup.select('#navitems-group1 > li.fore1 > a')
print(aTag)
print(aTag[0].string,aTag[0]['href'])
print('---------------')
group1 = soup.select('#navitems-group1')
group2 = soup.select('#navitems-group2')
group3 = soup.select('#navitems-group3')
for value in group1:
aTags = value.find_all(name="a")
for aTag in aTags:
print(aTag.string)
for value in group2:
aTags = value.find_all(name="a")
for aTag in aTags:
print(aTag.string)
for value in group3:
aTags = value.find_all(name="a")
for aTag in aTags:
print(aTag.string)
补充
上面只介绍了bs4对象中四种中的一种tag对象,且介绍了它的name,attrs属性和方法,还有select选择器(支持大部分的CSS选择器)
还有其他3种对象分别是
1.NavigableString(使用.string来提取字符串数据;使用replace_with()方法替换成其他的字符串)
2.BeautifulSoup(Beautiful对象是一个文档的全部内容,可以把他当作tag对象,它的.name属性是很有用的,烦恼会一个'docunment'字符串)
3.Comment(用于判断输入的字符串是不是注释部分的内容,注释内容的字符串类型返回一个bs4.element.Comment对象,这个对象等于type(comment))##comment为返回的字符串对象
多节点的情况可以使用.strings属性循环获取内容
for string in soup.strings:
print(repr(string))
repr() 函数将对象转化为供解释器读取的形式。
>>>s = 'RUNOOB'
>>> repr(s)
"'RUNOOB'"