您的位置:首页 > 理论基础 > 计算机网络

Python网络数据采集——BeautifulSoup

2017-08-21 13:10 274 查看
当米开朗基罗被问及如何完成《大卫》这样匠心独具的雕刻作品时,他有一段著名的回答:“很简单,你只要用锤子把石头上不像大卫的地方敲掉就行了。”

爬虫也是如此,有很多技巧可以帮我们“敲掉”网页上那些不需要的信息。下面介绍一些解析复杂的HTML 页面的方法。

1.BeautifulSoup

2.BeautifulSoup的find( )和findAll( )详解

3.其他BeautifulSoup对象

1. BeautifulSoup

每个网站都会有层叠样式表(Cascading Style Sheet,CSS)。虽然你可能会认为,专门为了让浏览器和人类可以理解网站内容而设计一个展现样式的层,是一件愚蠢的事,但是CSS 的发明却是网络爬虫的福音。CSS 可以让HTML 元素呈现出差异化。下面是一个标签:

<span class="green"></span>


另一些标签:

<span class="red"></span>


网络爬虫可以通过class 属性的值,轻松地区分出两种不同的标签。

下面创建一个网络爬虫来抓取http://www.pythonscraping.com/pages/warandpeace.html这个网页。

如下所示:

"<span class="red">Heavens! what a virulent attack!</span>" replied <span class=
"green">the prince</span>, not in the least disconcerted by this reception.


我们可以抓出整个页面,然后创建一个BeautifulSoup 对象。

from urllib.request import urlopen
from bs4 import BeautifulSoup
html = urlopen("http://www.pythonscraping.com/pages/warandpeace.html")
bsObj = BeautifulSoup(html)


通过BeautifulSoup 对象,我们可以用findAll()函数抽取只包含在
<span class="green"></span>
标签里的文字,这样就会得到一个人物名称的Python 列表(findAll() 非常灵活,会经常用到它):

nameList = bsObj.findAll("span", {"class":"green"})
for name in nameList:
print(name.get_text())


执行后会按照人物出场顺序显示所有人名。这是怎么实现的呢?之前,我们调用bsObj.tagName 只能获取页面中的第一个指定的标签。我们可以调用:

bsObj.findAll(tagName, tagAttributes)


可以获取页面中所有指定的标签,不只是第一个。获取人名列表之后,程序遍历列表中所有的名字,然后打印name.get_text(),就可以把标签中的内容分开显示了。

2. BeautifulSoup的find( )和findAll( )详解

BeautifulSoup 里的find() 和findAll() 可能是你最常用的两个函数。借助它们,你可以通过标签的不同属性轻松地过滤HTML 页面,查找需要的标签组或单个标签。 结构如下:

findAll(tag, attributes, recursive, text, limit, keywords)
find(tag, attributes, recursive, text, keywords)


很可能你会发现,在95% 的时间里自己都只需要使用前两个参数:tag 和attributes。但是,还是需要研究下所有的参数。

标签参数tag

可以传一个标签的名称或多个标签名称组成的Python的 列表 做标签参数。例如,下面的代码将返回一个包含HTML 文档中所有标题标签的列表:

.findAll({"h1","h2","h3","h4","h5","h6"})


属性参数attributes

属性参数attributes 是用一个Python 字典 封装一个标签的若干属性和对应的属性值。例如,下面这个函数会返回HTML 文档里红色与绿色两种颜色的span 标签:

.findAll("span", {"class":{"green", "red"}})


递归参数recursive

递归参数recursive是一个布尔变量。recursive =True,findAll 就会根据你的要求去查找标签参数的所有子标签,以及子标签的子标签。recursive=False,findAll 就只查找文档的一级标签。findAll默认是支持递归查找的(recursive 默认值是True);一般情况下这个参数不需要设置,除

非你真正了解自己需要哪些信息,而且抓取速度非常重要,那时你可以设置递归参数。

文本参数text

它是用标签的文本内容去匹配,而不是用标签的属性。假如我们

想查找前面网页中包含“the prince”内容的标签数量,我们可以把之前的findAll 方法换成下面的代码:

nameList = bsObj.findAll(text="the prince")
print(len(nameList))


范围限制参数limit

显然只用于findAll 方法。find 其实等价于findAll 的limit =1 时的情形。如果你只对网页中获取的前x 项结果感兴趣,就可以设置它。但是要注意,这个参数设置之后,获得的前几项结果是按照网页上的顺序排序的,未必是你想要的那前几项。

关键词参数keyword

可以让你选择那些具有指定属性的标签。例如:

allText = bsObj.findAll(id="text")
print(allText[0].get_text())


注意

虽然关键词参数keyword 在一些场景中很有用,但是,它是BeautifulSoup 在

技术上做的一个冗余功能。任何用关键词参数能够完成的任务,同样可以用

一些的技术解决,以后介绍。

下面两行代码是完全一样的:

bsObj.findAll(id="text")
bsObj.findAll("", {"id":"text"})


最重要的是!!!用keyword 偶尔会出现问题,尤其是在用class 属性查找标签的时候,因为class 是Python 中受保护的关键字。也就是说,class 是Python 语言的保留字,在Python 程序里是不能当作变量或参数名使用的(和前面介绍

的BeautifulSoup.findAll() 里的keyword 无关)。假如你运行下面的代码,Python 就会因为你误用class 保留字而产生一个语法错误:

bsObj.findAll(class="green")


不过,你可以用BeautifulSoup 提供的有点儿臃肿的方案,在class 后面增加一个下划线:

bsObj.findAll(class_="green")


另外,你也可以用属性参数把class 用引号包起来:

bsObj.findAll("", {"class":"green"})


回忆一下前面的内容,通过标签参数tag 把标签列表传到.findAll() 里获取一列标签,其实就是一个“或”关系的过滤器(即选择所有带标签1 或标签2 或标签3……的一列标签)。如果你的标签列表很长,就需要花很长时间才能写完。而关键词参数keyword 可以让你增加一个“与”关系的过滤器来简化工作。

3. 其他BeautifulSoup对象

前面已经说了BeautifulSoup 的两种对象了。

• BeautifulSoup 对象

前面代码示例中的bsObj

• 标签Tag 对象

BeautifulSoup 对象通过find 和findAll,或者直接调用子标签获取的一列对象或单个

对象,就像:

bsObj.div.h1


还有两种对象,不常用,可以了解一下。

• NavigableString 对象

用来表示标签里的文字,不是标签(有些函数可以操作和生成NavigableString 对象,而不是标签对象)。

• Comment 对象

用来查找HTML 文档的注释标签,这四个对象是你用BeautifulSoup 库时会遇到。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  python 爬虫