这是日常学python的第16篇原创文章
经过了前面几篇文章的学习,估计你已经会爬不少中小型网站了。但是有人说,前面的正则很难唉,学不好。正则的确很难,有人说过:如果一个问题用正则解决,那么就变成了两个问题。所以说学不会是很正常的,不怕,除了正则,我们还可以用另外一个强大的库来解析html。所以,今天的主题就是来学习这个强大的库--BeautifulSoup,不过正则还是需要多多练习下的。
因为是第三方库所以我们需要下载,在命令行敲下以下代码进行下载
pip install beautifulsoup4
安装第三方解析库
pip install lxml
pip install html5lib
如果不知道有什么用请往下看
1.相关解析库的介绍
这里官方推荐解析库为lxml,因为它的效率高。下面都是用lxml解析库来进行解析的。
2.详细语法介绍
本文是进行解析豆瓣图书首页
1)创建bs对象
from bs4 import BeautifulSoup
import requestsresponse = requests.get('https://book.douban.com/').text# print(response)# 创建bs对象soup = BeautifulSoup(response, 'lxml') # 使用到了lxml解析库2)获取相关标签
标签:
<a data-moreurl-dict='{"from":"top-nav-click-main","uid":"0"}' href="https://www.douban.com" target="_blank">豆瓣</a>
上面的a就是一个标签名字,最简单的就是<a></a>这样,可以简单理解为<>里面的第一个单词就是标签名
# 获取标签
print(soup.li) # 这个只是获取第一个li标签# 结果<li class=""><a data-moreurl-dict='{"from":"top-nav-click-main","uid":"0"}' href="https://www.douban.com" target="_blank">豆瓣</a></li>3)获取标签的名字和内容
标签的名字和内容:
<a >豆瓣</a>
如上面所说,a就是标签名字,而两个标签之中所夹杂的内容就是我们所说的内容,如上,豆瓣就是该标签的内容
# 获取标签名字
print(soup.li.name)# 获取标签内容print(soup.li.string) # 这个只能是这个标签没有子标签才能正确获取,否则会返回None# 结果liNone由于这个li标签里面还有个子标签,所以它的文本内容为None
下面这个就可以获取它的文本内容
# 获取标签内的标签
print(soup.li.a)print(soup.li.a.string) # 这个标签没有子标签所以可以获取到内容# 结果<a data-moreurl-dict='{"from":"top-nav-click-main","uid":"0"}' href="https://www.douban.com" target="_blank">豆瓣</a>豆瓣4)获取标签属性,有两种方法
标签属性:
<a href="https://www.douban.com" target="_blank">豆瓣</a>
可以简单理解为属性就是在标签名字旁边而且在前一个<>符号里面的,还有是有等号来进行体现的。所以上面的href就是标签属性名字,等号右边的就是属性的值,上面的值是个网址
# 获取标签属性
print(soup.li.a['href']) # 第一种print(soup.li.a.attrs['href']) # 第二种# 结果https://www.douban.comhttps://www.douban.com5)获取标签内的子标签
子标签:
<li><a>豆瓣</a></li>
比如我们现在获取的li标签,所以a标签就是li标签的子标签
# 获取标签内的标签
print(soup.li.a)# 结果<a data-moreurl-dict='{"from":"top-nav-click-main","uid":"0"}' href="https://www.douban.com" target="_blank">豆瓣</a>6)获取所有子节点
子节点:这个和子标签是差不多的,只不过这里是获取一个标签下的所有子标签,上面的只是获取最接近该标签的子标签
# 获取子节点
print(soup.div.contents) # 返回一个列表 第一种方法for n, tag in enumerate(soup.div.contents): print(n, tag)# 结果['\n', <div class="bd"><div class="top-nav-info"><a class="nav-login" href="https://www.douban.com/accounts/login?source=book" rel="nofollow">登录</a>...0 1 <div class="bd"><div class="top-nav-info">...这个是获取div下的所有子节点,.content就是获取子节点的属性
7)第二种方法获取所有子节点
# 第二种方法
print(soup.div.children) # 返回的是一个迭代器for n, tag in enumerate(soup.div.children): print(n, tag)这个是用.children获取所有的子节点,这个方法返回的是一个迭代器
8)获取标签的子孙节点,就是所有后代
子孙节点:
<ul>
<li><a>豆瓣</a></li></ul>从上面知道,li标签是ul标签的子标签,a标签是li标签的子标签,若此时我们获取的是ul标签,所以li标签和a标签都是ul标签的子孙节点
# 获取标签的子孙节点
print(soup.div.descendants) # 返回的是一个迭代器for n, tag in enumerate(soup.div.descendants): print(n, tag)# 结果...<generator object descendants at 0x00000212C1A1E308>0 1 <div class="bd"><div class="top-nav-info"><a class="nav-login" href="https://www.douban.com/accounts/login?source=book" rel="nofollow">登录</a>...这里用到了.descendants属性,获取的是div标签的子孙节点,而且返回结果是一个迭代器
9)获取父节点和所有祖先节点
既然有了子节点和子孙节点,反过来也是有父节点和祖先节点的,所以都很容易理解的
# 获取父节点
print(soup.li.parent) # 返回整个父节点# 获取祖先节点print(soup.li.parents) # 返回的是一个生成器for n, tag in enumerate(soup.li.parents): print(n, tag).parent属性是获取父节点,返回来的是整个父节点,里面包含该子节点。.parents就是获取所有的祖先节点,返回的是一个生成器
10)获取兄弟节点
兄弟节点:
<ul>
<li><a>豆瓣1</a></li><li><a>豆瓣2</a></li><li><a>豆瓣3</a></li></ul>比如上面的html代码,里面的li标签都是ul标签的子节点,而li标签都是处于同级的,所以上面的li标签都是各自的兄弟。这就是兄弟节点。
# 获取兄弟节点
print(soup.li.next_siblings) # 获取该标签的所有同级节点,不包括本身 返回的是一个生成器for x in soup.li.next_siblings: print(x)# 结果<generator object next_siblings at 0x000002A04501F308><li class="on"><a data-moreurl-dict='{"from":"top-nav-click-book","uid":"0"}' href="https://book.douban.com">读书</a></li>....next_siblings属性是获取该标签的所有在他后面的兄弟节点,不包括他本身。同时返回结果也是一个迭代器
同理,既然有获取他的下一个所有兄弟标签,也有获取他前面的所有兄弟标签
soup.li.previous_siblings
如果只是获取一个即可,可以选择把上面的属性后面的s字母去掉即可,如下
soup.li.previous_sibling # 获取前一个兄弟节点
soup.li.next_sibling # 获取后一个兄弟节点3.bs库的更高级的用法
在前面我们可以获取标签的名字、属性、内容和所有的祖孙标签。但是当我们需要获取任意一个指定属性的标签还是有点困难的,所以,此时有了下面这个方法:
soup.find_all( name , attrs , recursive , text , **kwargs )
name:需要获取的标签名
attrs:接收一个字典,为属性的键值,或者直接用关键字参数来替代也可以,下面
recursive:设置是否搜索直接子节点
text:对应的字符串内容
limit:设置搜索的数量
1)先使用name参数来进行搜索
# 先使用name参数
print(soup.find_all('li')) # 返回一个列表,所有的li标签名字# 结果[<li class=""><a data-moreurl-dict='{"from":"top-nav-click-main","uid":"0"}' href="https://www.douban.com" target="_blank">豆瓣</a></li>, <li class="on">...这里获取了所有标签名字为li的标签
2)使用name和attrs参数
# 使用name和attrs参数
print(soup.find_all('div', { 'class': 'more-meta'})) # 这个对上个进行了筛选,属性参数填的是一个字典类型的# 结果[<div class="more-meta"><h4 class="title"> 刺 </h4>...这里搜索了具有属性为class='more-meta'的div标签
3)根据关键字参数来搜索
# 对相关属性进行进行查找也可以这样
print(soup.find_all(class_='more-meta')) # 使用关键字参数,因为class是python关键字,所以关键字参数时需要加多一个下划线来进行区别# 结果和上面的结果一样...这里注意,我们找的是class属性为more-meta的标签,用了关键字参数,但是python里面有class关键字,所以为了不使语法出错,所以需要在class加个下划线
其他参数的就不再介绍了,可以自行去官网查看
4)find()方法
此方法与find_all()方法一样,只不过这个方法只是查找一个标签而已,后者是查找所有符合条件的标签。
还有很多类似的方法,用法都差不多,就不再一一演示了,需要的可以去官网查看
5)select()方法
这个方法是使用css选择器来进行筛选标签的。
css选择器:就是根据标签的名字,id和class属性来选择标签。
通过标签名:直接写该标签名,如 li a ,这个就是找li标签下的a标签
通过class属性:用. 符号加class属性值,如 .title .time 这个就是找class值为title下的class值为time的标签
通过id属性:用# 加id属性值来进行查找,如 #img #width 这个就是找id值为img下的id值为width的标签
上面三者可以混合使用,如 ul .title #width
如果还不太会的话,可以直接在浏览器上按下f12来查看
位置在箭头所指的位置就是选择器的表达
代码如下
# 还可以用标签选择器来进行筛选元素, 返回的都是一个列表
print(soup.select('ul li div')) # 这个是根据标签名进行筛选print(soup.select('.info .title')) # 这个是根据class来进行筛选print(soup.select('#footer #icp')) # 这个是根据id来进行筛选# 上面的可以进行混合使用print(soup.select('ul li .cover a img'))这里的获取属性和文本内容
# 获取属性
for attr in soup.select('ul li .cover a img'): # print(attr.attrs['alt']) # 也可以这样 print(attr['alt'])# 获取标签的内容for tag in soup.select('li'): print(tag.get_text()) # 里面可以包含子标签,会将子标签的内容连同输出.get_tex()方法和前面的.string属性有点不一样哈,这里的他会获取该标签的所有文本内容,不管有没有子标签
写在最后
以上的这些都是个人在学习过程中做的一点笔记。还有点不足,如果有错误的话欢迎大佬指出哈。如果想要查看更多相关用法可以去官方文档查看:
学习参考资料:https://edu.hellobi.com/course/157
如果这篇文章对你有用,点个赞,转个发如何?
还有,祝大家今天愚人节快乐
MORE延伸阅读◐◑
◐◑
◐◑
日常学python
代码不止bug,还有美和乐趣