python爬虫常用库之BeautifulSoup详解

这是日常学python的第16篇原创文章

经过了前面几篇文章的学习,估计你已经会爬不少中小型网站了。但是有人说,前面的正则很难唉,学不好。正则的确很难,有人说过:如果一个问题用正则解决,那么就变成了两个问题。所以说学不会是很正常的,不怕,除了正则,我们还可以用另外一个强大的库来解析html。所以,今天的主题就是来学习这个强大的库--BeautifulSoup,不过正则还是需要多多练习下的。

因为是第三方库所以我们需要下载,在命令行敲下以下代码进行下载

pip install beautifulsoup4

安装第三方解析库

pip install lxml
pip install html5lib

如果不知道有什么用请往下看

1.相关解析库的介绍

这里官方推荐解析库为lxml,因为它的效率高。下面都是用lxml解析库来进行解析的。

2.详细语法介绍

本文是进行解析豆瓣图书首页https://book.douban.com/

1)创建bs对象

from bs4 import BeautifulSoup
import requests
response = 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
# 结果
li
None

由于这个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.com
https://www.douban.com

5)获取标签内的子标签

子标签:

<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属性有点不一样哈,这里的他会获取该标签的所有文本内容,不管有没有子标签

写在最后

以上的这些都是个人在学习过程中做的一点笔记。还有点不足,如果有错误的话欢迎大佬指出哈。如果想要查看更多相关用法可以去官方文档查看:http://beautifulsoup.readthedocs.io/zh_CN/latest/

学习参考资料:https://edu.hellobi.com/course/157

如果这篇文章对你有用,点个赞,转个发如何?

还有,祝大家今天愚人节快乐

MORE

原文发布于微信公众号 - 日常学python(daily_learn)

原文发表时间:2018-04-01

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏机器学习实践二三事

Shell && Vim快捷键

刚买到了自己一直想买的HHKB pro 2 type S,不得不说手感什么的确实没让我失望,重点是Ctrl的位置很适合类unix环境啊,不会快捷键都不好意思敲这...

20850
来自专栏卡少编程之旅

四个Vue的写法优化技巧

36660
来自专栏前端布道

Angular开发实践(四):组件之间的交互

在Angular应用开发中,组件可以说是随处可见的。本篇文章将介绍几种常见的组件通讯场景,也就是让两个或多个组件之间交互的方法。 根据数据的传递方向,分为父组件...

51680
来自专栏超然的博客

事件委托和this

  事件的第一个阶段是捕获阶段。事件从文档的根节点流向目标对象节点。途中经过各个层次的DOM节点,并在各节点上触发捕获事件,直到到达事件的目标节点。捕获阶段的主...

14230
来自专栏超然的博客

不容忽略的——CSS规范

         当A、B、C、...它们类型相同且外形相似区别不大,那么就以它们中出现率最高的做成基类,其他做成基类的扩展。

12120
来自专栏JetpropelledSnake

Vue学习笔记之Vue指令系统介绍

所谓指令系统,大家可以联想咱们的cmd命令行工具,只要我输入一条正确的指令,系统就开始干活了。

10740
来自专栏AndroidTv

前端入门6-JavaScript客户端api&jQuery

作为一个前端小白,入门跟着这四个来源学习,感谢作者的分享,在其基础上,通过自己的理解,梳理出的知识点,或许有遗漏,或许有些理解是错误的,如有发现,欢迎指点下。

12840
来自专栏更流畅、简洁的软件开发方式

使用接口来统一控件的取值、赋值和初始化

      这里说的控件主要指的是文本框、下拉列表框这一类的控件,用户使用这些控件输入数据,然后我们需要提取这些数据进行处理。但是不同的控件有不同的取值方式,比...

20660
来自专栏Modeng的专栏

Vue2.5笔记:v-if 和 v-show指令

熟悉 Angular 的同学对指令肯定不会陌生,Vue中也借鉴了指令这一特性,在 Vue 中指令都是带有 v-的特殊属性,那么指令有什么作用呢?

9410
来自专栏从零开始学 Web 前端

从零开始学 Web 之 jQuery(八)each,多库共存,包装集,插件

each 方法用来遍历 jQuery 对象的,它的参数是一个事件处理函数,这个事件处理函数有两个参数,第一个参数是索引,第二个参数时索引对应的 DOM 对象,使...

12840

扫码关注云+社区

领取腾讯云代金券