来源:IT派
ID:
it_pai
Beautiful Soup
是一个可以从HTML或XML文件中提取数据的Python库,简单来说,它能将HTML的标签文件解析成树形结构,然后方便地获取到指定标签的对应属性。
通过Beautiful Soup库,我们可以将指定的class或id值作为参数,来直接获取到对应标签的相关数据,这样的处理方式简洁明了。
当前最新的 Beautiful Soup 版本为4.4.0,Beautiful Soup 3 当前已停止维护。
Beautiful Soup 4 可用于 Python2.7 和 Python3.0,本文示例使用的Python版本为2.7。
博主使用的是Mac系统,直接通过命令安装库:
sudo
easy_install beautifulsoup4
安装完成后,尝试包含库运行:
from
bs4
import
BeautifulSoup
若没有报错,则说明库已正常安装完成。
开始
本文会通过这个网页http://reeoo.com来进行示例讲解,如下图所示
BeautifulSoup 对象初始化
将一段文档传入 BeautifulSoup 的构造方法,就能得到一个文档对象。如下代码所示,文档通过请求url获取:
from bs4 import BeautifulSoup
import urllib2
url = 'http://reeoo.com'
request = urllib2.Request(url)
response = urllib2.urlopen(request, timeout=20)
content = response.read()
soup = BeautifulSoup(content, 'html.parser')
request 请求没有做异常处理,这里暂时先忽略。BeautifulSoup 构造方法的第二个参数为文档解析器,若不传入该参数,BeautifulSoup会自行选择最合适的解析器来解析文档,不过会有警告提示。
也可以通过文件句柄来初始化,可先将HTML的源码保存到本地同级目录 reo.html,然后将文件名作为参数:
soup
= BeautifulSoup(open(
'reo.html'
))
可以打印 soup,输出内容和HTML文本无二致,此时它为一个复杂的树形结构,每个节点都是Python对象。
Ps. 接下来示例代码中所用到的 soup 都为该soup。
Tag
Tag对象与HTML原生文档中的标签相同,可以直接通过对应名字获取
tag = soup.title
print tag
打印结果:
<
title
>
Reeoo - web design inspiration and website gallery
title
>
Name
通过Tag对象的name属性,可以获取到标签的名称
Attributes
一个tag可能包含很多属性,如id、class等,操作tag属性的方式与字典相同。
例如网页中包含缩略图区域的标签 article
...
<
article
class
=
"box"
>
<
div
id
=
"main"
>
<
ul
id
=
"list"
>
<
li
id
=
"sponsor"
>
<
div
class
=
"sponsor_tips"
>
div
>
<
script
async
type
=
"text/javascript"
src
=
"//cdn.carbonads.com/carbon.js?zoneid=1696&serve=CVYD42T&placement=reeoocom"
id
=
"_carbonads_js"
>
script
>
li
>
...
获取它 class 属性的值
tag = soup.article
c = tag['class']
print c
也可以直接通过 .attrs 获取所有的属性
tag = soup.article
attrs = tag.attrs
print attrs
ps. 因为class属于多值属性,所以它的值为数组。
tag中的字符串
通过 string 方法获取标签中包含的字符串
tag = soup.title
s = tag.string
print s
文档树的遍历
一个Tag可能包含多个字符串或其它的Tag,这些都是这个Tag的子节点。Beautiful Soup提供了许多操作和遍历子节点的属性。
子节点
通过Tag的 name 可以获取到对应标签,多次调用这个方法,可以获取到子节点中对应的标签。
如下图:
我们希望获取到 article 标签中的 li
tag = soup.article.div.ul.li
print tag
打印结果:
<
li
id
=
"sponsor"
>
<
div
class
=
"sponsor_tips"
>
div
>
<
script
async
=
""
id
=
"_carbonads_js"
src
=
"//cdn.carbonads.com/carbon.js?zoneid=1696&serve=CVYD42T&placement=reeoocom"
type
=
"text/javascript"
>
script
>
li
>
也可以把中间的一些节点省略,结果也一致
通过 . 属性只能获取到第一个tag,若想获取到所有的 li 标签,可以通过 find_all() 方法
ls
= soup.article.div.ul.find_all(
'li'
)
获取到的是包含所有li标签的列表。
tag的 .contents 属性可以将tag的子节点以列表的方式输出:
tag
= soup.article.div.ul
contents
= tag.contents
打印 contents 可以看到列表中不仅包含了 li 标签内容,还包括了换行符 '\n'
过tag的 .children 生成器,可以对tag的子节点进行循环
tag = soup.article.div.ul
children = tag.children
print
children
for
child
in
children:
print
child
可以看到 children 的类型为
.contents 和 .children 属性仅包含tag的直接子节点,若要遍历子节点的子节点,可以通过 .descendants 属性,方法与前两者类似,这里不列出来了。
父节点
通过 .parent 属性来获取某个元素的父节点,article 的 父节点为 body。
tag = soup.article
print tag.parent.name
或者通过 .parents 属性遍历所有的父辈节点。
tag = soup.article
for
p
in
tag.parents:
print
p.name
兄弟节点
.next_sibling 和 .previous_sibling 属性用来插叙兄弟节点,使用方式与其他的节点类似。
文档树的搜索
对树形结构的文档进行特定的搜索是爬虫抓取过程中最常用的操作。
find_all()
find_all(name , attrs , recursive , string , ** kwargs)
name 参数
查找所有名字为 name 的tag
soup.find_all('title')
# [
<
title
>
Reeoo - web design inspiration and website gallery
title
>]
soup.find_all('footer')
# [
<
footer
id
=
"footer"
>
\n
<
div
class
=
"box"
>
\n
<
p
>
...
div
>\n
footer
>]
keyword 参数
如果指定参数的名字不是内置的参数名(name , attrs , recursive , string),则将该参数当成tag的属性进行搜索,不指定tag的话则默认为对所有tag进行搜索。
如,搜索所有 id 值为 footer 的标签
soup.find_all(id='footer')
# [
<
footer
id
=
"footer"
>
\n
<
div
class
=
"box"
>
\n
<
p
>
...
div
>\n
footer
>]
加上标签的参数
soup.find_all(
'footer'
, id=
'footer'
)
# [
获取所有缩略图的 div 标签,缩略图用 class 为 thumb 标记
soup.find_all(
'div'
,
class
_
=
'thumb'
)
这里需要注意一点,因为 class 为Python的保留关键字,所以作为参数时加上了下划线,为“class_”。
指定名字的属性参数值可以包括:字符串、正则表达式、列表、True/False。
True/False
是否存在指定的属性。
搜索所有带有 target 属性的标签
soup.find_all(target=
True
)
搜索所有不带 target 属性的标签(仔细观察会发现,搜索结果还是会有带 target 的标签,那是不带 target 标签的子标签,这里需要注意一下。)
soup.find_all(target=
False
)
可以指定多个参数作为过滤条件,例如页面缩略图部分的标签如下所示:
...
<
li
>
<
div
class
=
"thumb"
>
<
a
href
=
"http://reeoo.com/aim-creative-studios"
>
![AIM Creative Studios](http://img2.jintiankansha.me/get6?src=http://upload-images.jianshu.io/upload_images/1346917-f6281ffe1a8f0b18.gif?imageMogr2/auto-orient/strip)
a
>
div
>
<
div
class
=
"title"
>
<
a
href
=
"http://reeoo.com/aim-creative-studios"
>
AIM Creative Studios
a
>
div
>
li
>
...
搜索 src 属性中包含 reeoo 字符串,并且 class 为 lazy 的标签:
soup.find_all(src=re.compile(
"reeoo.com"
),
class
_
=
'lazy'
)
搜索结果即为所有的缩略图 img 标签。
有些属性不能作为参数使用,如 data-**** 属性。在上面的例子中,data-original 不能作为参数使用,运行起来会报错,SyntaxError: keyword can't be an expression*。
attrs 参数
定义一个字典参数来搜索对应属性的tag,一定程度上能解决上面提到的不能将某些属性作为参数的问题。
例如,搜索包含 data-original 属性的标签
print
soup.find_all(attrs={
'data-original'
:
True
})
搜索 data-original 属性中包含 reeoo.com 字符串的标签
soup.find_all(attrs={
'data-original'