送上一碗鸡汤——BeautifulSoup网页解析

写在前面

上期讲述了正则表达式在网页解析中的用法,了解了怎么使用正则表达式去获取网页数据。然而正则表达式语法相对较难掌握以及正则表达式书写相对繁琐,所以就相继有了其它一些网页解析方式。本期就和大家讲一下其中的代表BeautifulSoup,其清晰便捷的语法规则真的如其名字所彰显的那样(美丽心灵都不会作恶呀),下面正式开始对其的讲解。

BeautifulSoup简介

BeautifulSoup是python中的一个网页解析库,通过它可以方便地从网页中提取数据,因为其简单便捷的用法,使其已经成为和lxml以及html6lib一样出色的python解析库,熟练掌握它可以显著提高你的网页解析效率。然而在使用它之前,请确保你已经正确安装了这个第三方库,如果你安装的是Anaconda,那么这个库已经默认安装,你也就不再需要做什麼安装准备了,如果你是直接安装的python,那么请通过pip命令正确去安装这个库,关于它的安装,小编在前期文章中已有介绍,这里就不再赘述。考虑到beautifulsoup的使用依赖于相应的解析器,小编这里建议大家也进行一下lxml库的安装。目前来说,在beautifulsoup中存在四种解析方式,如下:

解析器

使用方法

优势

劣势

所以通过对比可以发现,lxml解析方式存在一定优势,因此小编建议在使用过程中采用这种解析方式。

基本用法

小编这里以小猪租房网页获取来说明beautifulsoup用法。以广州房源中某处房源网页来说明,可以直接输入https://gz.xiaozhu.com/fangzi/36146931503.html进入这处房源的详细信息页面,而后通过右键检查命令进入网页源码界面,这里小编以获取房源的名称为例来进行说明。

小猪租房网

可以看出房源的名称与所对应的HTML代码均在红线框中,如果要获得房源名称,则需要根据这一名称在网页代码中的位置进行提取。通过观察可以发现,房源名称位于h4标签之下,所以可以依据这个标签进行获取,见下面代码。

importrequests

frombs4importBeautifulSoup

url ='https://gz.xiaozhu.com/fangzi/36146931503.html'

res = requests.get(url)

bsobj = BeautifulSoup(res.text,'lxml')

print(bsobj.h4.string)

这里主要从倒数第二行开始讲起。使用Beautifulsoup解析网页首先需要进行初始化,也就是这里的倒数第二行,通过初始化将初始化后的结果保存在bsobj变量中,下面就可以使用beautifulsoup中的正确方法进行数据获取了。因为需要获得的数据位于h4标签下,并且整个网页中仅有一个h4标签,所以完全可以通过命令索引的方式进行数据提取。也就是通过使用bsobj.h4获取,这样便获取了名称信息。然而这种命令获得的数据包含了em标签,所以这里进一步通过string命令提取出其中的文字信息。通过beautifulsoup提取网页信息就是两点:确定数据在网页代码中的位置;使用相应命令选择出这部分网页代码进而获得数据。第一点可以通过右键检查命令确定,下面讲述提取数据。

节点选择器

如果单个节点结构层次非常明显,则可以通过节点选择器来进行数据的获取,上文提到的选择方式就是节点选择器。小编这里首先给出一个网页代码例子,而后的操作基于这部分网页代码。

html ="""

The Dormouse's story

The Dormouse's story

Once upon a time there were three little sisters;and their names were

,

Lacie

and

Tillie

;

and they lived at the bottom of a well.

"""

选择元素

下面通过以上网页代码来说明节点选择器的用法。

frombs4importBeautifulSoup

soup = BeautifulSoup(html,'lxml')

print(soup.title)

print(type(soup.title))

print(soup.title.string)

print(soup.head)

print(soup.p)

# 输出结果

The Dormouse's story

The Dormouse's story

The Dormouse's story

The Dormouse's story

这里首先打印title节点的选择结果,输出结果正是title节点里面的文字内容。接下来,输出它的类型,是bs4.element.Tag类型,这是BeautifulSoup中一个重要的数据结构。经过选择器选择后,选择结果都是这种Tag类型。Tag具有一些属性,比如string属性,调用该属性,可以得到节点的文本内容,所以接下来的输出结果正是节点的文本内容。

接下来,选择head节点,结果也是节点内部的所有内容。最后选择p节点。这次情况比较特殊,发现结果是第一个p节点的内容,后面几个p节点并没有选到。也就是说,当有多个节点时,这种选择方式只会选择到第一个匹配的节点,其他的后面节点都会忽略。

提取信息

上面演示了调用string属性来获取文本的值,那么如何获取节点属性的值呢?如何获取节点名呢?下面来统一梳理一下信息的提取方式。

(1)获取名称

可以利用name属性获取节点的名称。这里还是以上面的文本为例,选取title节点,然后调用name属性就可以得到节点名称:

(2)获取属性

每个节点可能有多个属性,比如id和class等,选择这个节点元素后,可以调用attrs 获取所有属性:

print(soup.p.attrs)

print(soup.p.attrs['name'])

# 输出结果

{'class':['title'],'name':'dromouse'}

dromouse

可以看到,attrs的返回结果是字典,它把选择节点的所有属性和属性值组合成一个字典。接下来,如果要获取name属性,就相当于从字典中获取某个键值,只需要用中括号加属性名就可以了。比如,要获取name属性,就可以通过attrs['name']来得到。其实这样有点烦琐,还有一种更简单的获取方式:可以不用写attrs,直接在节点元素后面加中括号,传入属性名就可以获取属性值了。

(3)获取内容

利用string属性获取节点元素包含文本内容,比如要获取第一个p节点的文本:

print(soup.p.string)

# 运行结果如下:

The Dormouse's story

再次注意这里选择的p节点是第一个p节点,获取的文本也是第一个p节点里面的文本。

嵌套选择

在上面的例子中,每一个返回结果都是bs4.element.Tag类型,它同样可以继续调用节点进行下一步的选择。比如,获取了head节点元素,可以继续调用title来选取其内部的title节点元素:

关于节点选择器还有子节点、子孙节点、父节点、祖先节点等方面的知识,不过因为在实践中使用较少,这里就不再多讲了,下面讲一下方法选择器。

方法选择器

前面所讲的选择方法都是通过属性来选择的,这种方法非常快,但是如果进行比较复杂的选择的话,它就比较烦琐,不够灵活了。幸好,BeautifulSoup还提供了一些查询方法,比如find_all()和find()等,调用它们,然后传入相应的参数,就可以灵活查询了。

find_all()函数

find_all,顾名思义,就是查询所有符合条件的元素。给它传入一些属性或文本,就可以得到符合条件的元素,它的功能十分强大。其使用方法为:

find_all(name,attrs,recursive,text,**kwargs)

在使用过程中,最常用的两个参数就是前两个。name就是标签名称,attrs为属性值,表现为一个字典对。

(1)根据名字选择

html="""

Hello

Foo

Bar

Jay

Foo

Bar

"""

frombs4importBeautifulSoup

soup = BeautifulSoup(html,'lxml')

print(soup.find_all(name ='ul'))

print(type(soup.find_all(name='ul')[]))

# 输出结果

[

Foo

Bar

Jay

,

Foo

Bar

]

这里调用了find_all()方法,传入name参数,其参数值为ul。返回结果为列表,每个元素依然都是bs4.element.Tag类型。因为都是Tag类型,所以依然可以进行嵌套查询。还是同样的文本,这里查询出所有ul节点后,再继续查询其内部的li节点:

forulinsoup.find_all(name ='ul'):

print(ul.find_all(name='li'))

# 运行结果如下:

[

Foo

,

Bar

,

Jay

]

[

Foo

,

Bar

]

当然这些获得的数据均带有标签,可以通过get_text()函数仅仅获得文本内容。

(2)根据属性值选择

print(soup.find_all(attrs ={'id':'list-1'}))

print(soup.find_all(attrs ={'name':'elements'}))

# 输出结果

[

Foo

Bar

Jay

]

[

Foo

Bar

Jay

]

这里查询的时候传入的是attrs参数,参数的类型是字典类型。比如,要查询id为list-1的节点,可以传入attrs ={'id':'list-1'}的查询条件,得到的结果是列表形式,包含的内容就是符合id为list-1的所有节点。在上面的例子中,符合条件的元素个数是l ,所以结果是长度为l的列表。

find()函数

除了find_all()方法,还有find()方法,只不过后者返回的是单个元素,也就是第一个匹配的元素,而前者返回的是所有匹配的元素组成的列表。至于用法,和find_all()函数相似,这里简单举个例子:

html="""

Hello

Foo

Bar

Jay

Foo

Bar

"""

frombs4importBeautifulSoup

soup = BeautifulSoup(html,'lxml')

print(soup.find(name='ul'))

print(type(soup.find(name ='ul')))

print(soup.find(class_='list'))

# 运行结果如下:

Foo

Bar

Jay

Foo

Bar

Jay

这里返回结果不再是列表,而是第一个匹配的节点元素,类型依然是Tag类型。

另外,还有许多查询方法,其用法与前面介绍的find_all()、find()方法完全相同, 只不过查询范围不同,这里简单说明一下。

find_parents()和find_parent():前者返回所有祖先节点,后者返回直接父节点。

find_next_siblings()和find_next_ sibling():前者返回后面所有的兄弟节点,后者返回后面第一个兄弟节点。

find_previous_siblings()和find_previous_sibling():前者返回前面所有的兄弟节点,后者返回前面第一个兄弟节点。

find_all_next()和find_next():前者返回节点后所有符合条件的节点,后者返回第一个符合条件的节点。

find_all_previous()和find_previous():前者返回节点后所有符合条件的节点,后者返回第一个符合条件的节点。

除了这种选择方法,还有一种CSS选择方法,考虑到这种方法与后面介绍的解析工具方法类似,这里就不再多做介绍了,留待后文介绍。

后记

本文讲到这里就暂告一段落了,本期文章和大家聊了一下BeautifulSoup解析库的用法,主要介绍了其中的两种选择方法,这为快速抓取奠定了基础。后续文章会继续介绍另外几种网页解析的工具,敬请期待。最后再次感谢你们的支持与鼓励,你们的陪伴是小编前进的动力!

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180914G227P500?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券