beautifulsoup(以下简称bs),是一款网页结构解析模块,它支持传统的Xpath,css selector 语法,可以说很强大了,下面我们就来着重介绍下它的用法。
bs 可以使用pip 或者easy_install安装,方便快捷。
pip install Beautifulsoup4
一般就是先由requests 获取到网页源码后然后对页面进行解析,如图:
这样就基本上拿到了页面的源码了。
1.根据标签直接获取元素,如下图:
2.根据find,find_all方法查找
前者返回一个结果,后者返回所有结果
find( name , attrs , recursive , text , **kwargs )
name :要查找的标签名(字符串、正则、方法、True)
attrs: 标签的属性
recursive: 递归
text: 查找文本
**kwargs :其它 键值参数
因为class是关键字,所以要写成class_="value", 等同于attrs={"class":"value"}
这里的参数适用于find find_all两种方法。
只不过find_all 还有其他参数,比如限制查找返回数量 的limit方法,标签内容string方法。
3.根据select方法查找
soup.select('div') | 所有名为<div>的元素 |
soup.select('#aa') | 所有 id 属性名为aa的元素 |
soup.select('.oo') | 所有class 属性名为oo的元素 |
soup.select('div p') | 所有在<div>元素之内的<p>元素 |
soup.select('div >p') | 所有直接在<div>元素之内的<p>元素,中间没有其他元素 |
soup.select('input[name]') | 所有名为<input>,并有一个 name 属性,其值无所谓的元素 |
soup.select('input[type="button"]') | 所有名为<input>,并有一个 type 属性,其值为 button 的元素 |
soup.select('a')[0].get_text() # 获取首个a元素的文本
soup.select('a')[0].attrs['href'] # 获取首个a元素的链接地址
4.关系节点名
find_parents()返回所有祖先节点的列表,find_parent()返回直接父节点
print(soup.title.find_parent())
print(soup.title.find_parent().find_all('link')[1])
print(soup.title.find_parents())
find_next_siblings()返回后面所有兄弟节点的列表,find_next_sibling()返回后面第一个兄弟节点
print(soup.title.find_next_sibling())
print(soup.title.find_next_siblings())
find_previous_siblings()返回前面所有兄弟节点的列表,find_previous_sibling()返回前面第一个兄弟节点
print(soup.title.find_previous_sibling())
print(soup.title.find_previous_siblings())
find_all_next()返回节点后所有符合条件的节点的列表, find_next()返回节点后第一个符合条件的节点
print(soup.title.find_next('link'))
print(soup.title.find_all_next('link'))
find_all_previous()返回节点前所有符合条件的节点, find_previous()返回节点前第一个符合条件的节点
print(soup.title.find_previous('link'))
print(soup.title.find_all_previous('link'))
5.对象种类
tag(标签) navigablestring(标签内字符串) beautifulsoup(对象) comment(备注)
rep=requests.get('https://book.qidian.com/info/1014243481#Catalog',timeout=3)
soup=BeautifulSoup(rep.text,'html.parser')
print(soup.name) #beautifulsoup 对象
tr=soup.div
print(type(tr),tr) #tag对象 标签
print(tr.get_attribute_list('class')) #获取属性对应列表
print(tr.a.string) #navigablestring 对象,获取标签内文字,可使用str()方法将她转换为unicode字符串
print(soup.a.string.replace_with('fdf')) #替换navigablestring
comment 即为提取的注释内容,一般为!--xxxxxxx--! 包裹的内容就是了
三、使用案例
爬取起点小说主页第一页所有小说名字和链接,如图:
import requests
from bs4 import BeautifulSoup
rep=requests.get('https://www.qidian.com/all',timeout=3)
soup=BeautifulSoup(rep.text,'html.parser')
#按照步骤一步一步来爬取
ul=soup.find_all('ul','all-img-list cf')
for y in ul:
for z in y.find_all('div','book-mid-info'):
for x in z.find_all('h4'):
for v in x.find_all('a'):
print(v.get_text(),'https:'+v.attrs['href']) #获取a标签的内容和href属性值
最后就可以得出正确结果,如图:
关于bs大致就这么多,大家学会了吗??
今天就讲这么多,关于BS的强大之处,远不止于此,本文只是介绍了它的安装和基本用法,并通过一个案例来帮助大家加深理解,希望大家好好利用,在爬虫路上可以事倍功半!
------------------- End -------------------
最后多说一句,小编是一名python开发工程师,这里有我自己整理了一套最新的python系统学习教程,包括从基础的python脚本到web开发、爬虫、数据分析、数据可视化、机器学习等。想要这些资料的可以关注小编,并在后台私信小编:“01”即可领取。
上一节中,认识了Python中的lxml库,可以通过XPath来寻找页面中的位置,这也是仅仅对于结构完整的页面,但是对于有针对性的获取内容的时候并不很友好,比如说链接中以XXX开头或者结尾,而且中间符合某些特定规则,所以这时候需要认识一个新朋友,那就是另外一个很强大的解析库——Beautiful Soup。
与 lxml 一样,Beautiful Soup 也是一个HTML/XML的解析器,通过解析文档为用户提供需要抓取的数据的功能。
Beautiful Soup也有很多版本,不过Beautiful Soup3已经停止更新了,目前最新的都是Beautiful Soup4,而且也已经移植到bs4库中,我们安装bs4库后就可以直接使用。安装库使用pip安装,安装命令:
pip install beautifulsoup4
Beautiful Soup中支持的解析器有很多种,不仅仅支持Python标准库中的HTML解析器,还可以使用一些第三方的解析器,比如说lxml等,如表所示,是几种常见的解析器的优缺点。
解析器 | 使用方式 | 优点 | 缺点 |
Python标准库 | BeautifulSoup(html, "html.parser") | Python的内置标准库、文档容错性较强 | 执行速度适中 |
lxml解析器 | BeautifulSoup(html, "lxml") | 速度快、文档容错性较强 | 依赖C语言库 |
html5lib | BeautifulSoup(html, "html5lib") | 以浏览器的方式解析文档、容错性最好 | 执行速度慢 |
一般情况下可以使用Python标准库或者lxml作为常用的解析器,对于爬虫来说,比起速度来说,准确性的要求并不是很高。如果在解析文档上花费的时间太多,必然会导致爬虫的效率低。
Python标准库解析器并不需要安装,因为本身自带的,lxml解析器在上一节使用它作为解析器时候已经安装过了,也不需要额外安装,直接使用即可。html5lib的安装跟BeautifulSoup一样,使用pip安装:
pip install html5lib
from bs4 import BeautifulSoup
from lxml import etree
text='''
<html>
<head>
<title>实例HTML</title>
</head>
<body>
<div>
<h1>这是标题</h1>
</div>
<div>
<ul>
<li class="c1"><a href="link1.html" title="链接1">第一个链接</a></li>
<li class="c2"><a href="link2.html" title="链接2">第二个链接</a></li>
<li class="c3"><a href="link3.html" title="链接3">第三个链接</a></li>
</ul>
</div>
</body>
</html>
'''
# 生成一个BeautifulSoup对象
soup=BeautifulSoup(text, 'html.parser')
# 对象类型
print(type(soup))
#代码结果:
<class 'bs4.BeautifulSoup'>
现在就获得了一个BeautifulSoup的对象,Beautiful Soup其实是将HTML文档转换成一个复杂的树形结构,每个节点都是Python中的对象,所有对象可以归纳为 4 种:Tag、NavigableString、BeautifulSoup、Comment,后两种根本上讲也是前面两种的特殊情况。下面我们简要讲解这几个对象。
Tag
Tag是最容易理解的,跟字面意思一样,就是HTML中的标签。比如:一个a标签就是一个对象:
<a href="link1.html" title="链接1">第一个链接</a>
在tag对象中比较重要的两个属性name和attrs。通过这两个属性可以获取到标签中的信息:
print(soup.a.name)
print(soup.a.attrs)
#代码结果:
a
{'href': 'link1.html', 'title': '链接1'}
name其实就是获取标签的名称,这个是使用的不多,毕竟在日常使用的时候都会知道需要找哪些标签中的内容。attrs获取是标签中的属性,结果是一个字典类型的集合。
NavigableString
在上面两个属性中,并没法获取标签中的内容,那么NavigableString就是用来获取标签中文本内容的,用法也比较简单,直接使用string即可。
print(soup.a.string)
print(type(soup.a.string))
#代码结果:
第一个链接
<class 'bs4.element.NavigableString'>
BeautifulSoup
这个对象在前面提到过,表示一个页面(文档)的内容,可以作为一个特殊的Tag。
print(type(soup))
#代码结果:
<class 'bs4.BeautifulSoup'>
Comment
Comment对象也是一个特殊的NavigableString,读取的内容是注释里面的内容。把上面示例中的第一个a标签的内容更改成如下:
<a href="link1.html" title="链接1"><!--Hello--></a>
print(soup.a.string)
print(type(soup.a.string))
#代码结果:
Hello
<class 'bs4.element.Comment'>
注意:如果在标签内的文本既有正常文字也有注释,这时候string属性就无法获取到内容:
<a href="link1.html" title="链接1">第一个链接<!--Hello--></a>
print(soup.a.string)
#代码结果:
None
获取文本内容可以使用text方法,虽然text和string结果都是字符串,但是两个对象其实并不相同。
<a href="link1.html" title="链接1">第一个链接<!--Hello--></a>
print(soup.a.text)
print(type(soup.a.text))
#代码结果:
第一个链接
<class 'str'>
把HTML内容解析成为一个BeautifulSoup对象后,对这个对象的操作才是BeautifulSoup这个模块所能体验的强大之处。本身BeautifulSoup本身有着丰富的节点遍历功能,包括父节点、子节点、子孙节点的获取和逐个元素的遍历。
不过在实际应用上,我们使用遍历的还是少数,使用搜索的还是多数,现在很多网页中的元素很丰富,我们很少会把一个页面中的所有内容都获取下来,基本是需要的重点内容,这对于遍历来说,搜索更加显得便捷实用。
find_all()
说到搜索,最常使用的肯定是BeautifulSoup的find_all()方法,它会搜索当前 tag 的所有 tag 子孙节点,并判断每个节点是否符合过滤器的条件。
find_all()方法的完整参数为find_all(name, attrs, recursive, text,limit, **kwargs):
name:标签名称的过滤,支持正则
attrs:标签的属性条件的过滤,支持正则;
recursive:bool选项,如果为True,find_all()将遍历所有节点,否则只有子节点,默认为True;
text:标签中的文本过滤,;
limit:搜索限制过滤,如果不为空,表示找到指定数量的元素后将停止搜索,默认为空,将搜索全部;
kwargs:表示可以添加多个属性值参数过滤。
1.name参数
搜索所有a标签
links=soup.find_all('a')
print(links)
代码结果:
[<a href="link1.html" title="链接1">第一个链接</a>, <a href="link2.html" title="链接2">第二个链接</a>, <a href="link3.html" title="链接3">第三个链接</a>]
搜索所有名字带“a”标签
links=soup.find_all(re.compile(".*a.*"))
print(links)
代码结果(head和a标签都符合)
[<head>
<title>实例HTML</title>
</head>, <a href="link1.html" title="链接1">第一个链接</a>, <a href="link2.html" title="链接2">第二个链接</a>, <a href="link3.html" title="链接3">第三个链接</a>]
2. arrts参数
搜索所有a标签中title值为“链接1”
links=soup.find_all('a', attrs={"title": "链接1"})
print(links)
代码结果:
[<a href="link1.html" title="链接1">第一个链接</a>]
3. kwargs参数:
搜索所有a标签中herf中带“1”的标签
links=soup.find_all('a', href=re.compile(".*1.*"))
print(links)
代码结果:
[<a href="link1.html" title="链接1">第一个链接</a>]
4. text参数:
#搜索所有a标签中,文本带“二”的标签
links=soup.find_all('a', text=re.compile(".*二.*"))
print(links)
代码结果:
[<a href="link2.html" title="链接2">第二个链接</a>]
如果不加a标签,搜索的内容则仅仅是文本。
#搜索所有a标签中,文本带“二”的标签
links=soup.find_all('text=re.compile(".*二.*"))
print(links)
代码结果:
['第二个链接']
5. limit参数
#搜索所有a标签中,超链接以link开头,最多2个
links=soup.find_all('a', href=re.compile("link.*"), limit=2)
print(links)
代码结果:
[<a href="link1.html" title="链接1">第一个链接</a>, <a href="link2.html" title="链接2">第二个链接</a>]
find()
find()方法相当于给find_all()方法默认添加limit=1,仅仅发挥符合条件的第一个Tag。方便有时候我们仅仅需要一个值的时候,直接可以调用。参数跟find_all()一样,用法也是相同。
Beautiful Soup中用select()方法来CSS样式的进行筛选,当然也可以筛选标签。在标签的属性中,class的属性就是当前标签的CSS样式,返回的结果同样也是list。
1.通过标签名查找
查找所有a标签
links=soup.select('a')
print(links)
代码结果:
[<a href="link1.html" title="链接1">第一个链接</a>, <a href="link2.html" title="链接2">第二个链接</a>]
2.通过CSS样式类名查找
查找样式类名为c1的标签
links=soup.select('.c1')
print(links)
代码结果:
[<li class="c1"><a href="link1.html" title="链接1">第一个链接</a></li>]
3.通过标签属性查找
查找属性中href="link1.html"的a标签
links=soup.select('a[href="link1.html"]')
print(links)
代码结果:
[<a href="link1.html" title="链接1">第一个链接</a>]
在标签+属性组合中,属性不支持正则表达式。
4.获取查找到的内容
除了以上集中还可以使用标签的id等元素来进行查找,但是不管使用哪种方式,最终的是回去标签的内容或者属性中的值,那么找到相应的标签后,怎么取值呢?如果是去标签属性值,跟使用字典取值方式一样。如果是获取标签的文本,直接使用get_text()方法,可以获取到标签的文本内容。
查找属性中href="link1.html"的a标签
links=soup.select('a[href="link1.html"]')
#打印标签中的超链接值
print(links[0][‘href])
#打印标签文本内容
print(links[0].get_text())
代码结果:
第一个链接
link1.html
不管是使用lxml还是Beautiful Soup,多数结果都是获取文本内容或者是标签的属性值。文本内容多数是需要获取的内容,整理下来放到list中,最后可能保存本地文件或者数据库,而标签的中属性值多数可以找到子链接(详情链接),知道了怎么定位和获取页面的元素,下面我们就可以动手爬取页面的内容了。
eautifulsoup介绍:
第一步:安装BeautifulSoup4,lxml
pip install BeautifulSoup4
BeautifulSoup 是一个可以从HTML或XML文件中提取数据的Python库
pip install lxml
lxml 是一种使用 Python 编写的解析库,可以迅速、灵活地处理 XML 和 HTML
第二步:导包,from bs4 import BeautifulSoup
第三步:实例化对象
html="""
<html>
<head>
<title>The Dormouse's story</title>
</head>
<body>
<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">
<span>Elsie</span>
</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, 'lxml') # h:要解析的内容 lxml:解析器
知识补充:print(soup.prettify()) # 代码补全
第四步:打印
一、通过标签选取,会返回包含标签本身及其里面的所有内容
# print(soup.head) # 包含head标签在内的所有内容
# print(soup.p) # 返回匹配的第一个结果
1.print(soup.head)打印结果:
<head>
<title>The Dormouse's story</title>
</head>
2.print(soup.p)打印结果:
<p class="title" name="dromouse"><b><span>The Dormouse's story</span></b></p>
二、打印标签中间的文本内容,不包含<>
# .string是属性,作用是获取字符串文本
print(soup.html.head.title.string)
print(soup.title.string)
打印结果都为:The Dormouse's story
三、打印标签名
.name --获取标签本身名称
print(soup.title.name)
打印结果为:title
四、打印属性的值
.attrs[] --通过属性拿属性的值
print(soup.p.attrs['name'])# 获取p标签name属性的属性值
print(soup.a.attrs['id']) # 获取p标签id属性的属性值
print(soup.a['id']) #第二种写法
print(soup.p['class']) # 以列表得形式保存
print(soup.a['href']) # 也是只返回第一个值
1.print(soup.p.attrs['name'])打印结果:
dromouse
2.print(soup.p.attrs['id'])和print(soup.a['id'])打印结果:
link1
3.print(soup.p['class'])打印结果:
['title', 'asdas']
4.print(soup.a['href'])打印结果:
http://example.com/elsie
五、打印父标签下的所有子标签
.contents 获取标签子节点,以列表形式返回
.children 获取子节点,返回的是一个list类型的迭代器
print(soup.body.contents) # a是p的子节点,获取P标签所有子节点内容 返回一个list
print(soup.body.children) #返回的是一个list类型的迭代器
1.print(soup.body.contents)的打印结果:
['\n', <p class="title asdas" name="dromouse"><b><span>The Dormouse's story</span></b></p>, '\n', <p class="story">Once upon a time there were three little sisters; and their names were
<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>,
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>, '\n', <p class="story">...</p>, '\n']
2.print(soup.body.children)的打印结果:
<list_iterator object at 0x000002035ECC7088>
.children 获取子节点讲解
1.和for循环一起使用
for i in soup.p.children:
print(i)
打印结果:
*请认真填写需求信息,我们会在24小时内与您取得联系。