龙空技术网

数据获取:​网页解析之BeautifulSoup

马拉松程序员 106

前言:

眼前兄弟们对“html数据分析”都比较珍视,兄弟们都想要剖析一些“html数据分析”的相关资讯。那么小编也在网摘上收集了一些对于“html数据分析””的相关文章,希望小伙伴们能喜欢,我们一起来学习一下吧!

在上一节中,认识了Python中的lxml库,可以通过XPath来寻找页面中的位置,这也是仅仅对于结构完整的页面,但是对于有针对性的获取内容的时候并不很友好,比如说链接中以XXX开头或者结尾,而且中间符合某些特定规则,所以这时候需要认识一个新朋友,那就是另外一个很强大的解析库——Beautiful Soup。

与 lxml 一样,Beautiful Soup 也是一个HTML/XML的解析器,通过解析文档为用户提供需要抓取的数据的功能。

安装BeautifulSoup

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 BeautifulSoupfrom lxml import etreetext = '''<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()一样,用法也是相同。

CSS选择器

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中,最后可能保存本地文件或者数据库,而标签的中属性值多数可以找到子链接(详情链接),知道了怎么定位和获取页面的元素,下面我们就可以动手爬取页面的内容了。

标签: #html数据分析