【爬虫学习笔记】BeautifulSoup用法分析(二)


本文章介绍BeautifulSoup的主要函数用法,对于BeautifulSoup的概念介绍请点击下方分析()查阅或自行查询,此处不再赘述。为了方便文章的编写,下文将用BS代表BeautifulSoup。


笔记分为以下两篇文章:

BeatifulSoup用法分析(

BeatifulSoup用法分析()-本文


上一部分主要介绍BS最基本,也是实战中很少用到的函数,这部分我们开始介绍BS的常见函数用法

1、导入模块

from bs4 import BeautifulSoup

2、创建一个html字符串用来模拟网页

html = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title" name="dromouse"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1"><!-- Elsie --></a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
"""

3、创建BS对象

hello = BeautifulSoup(html,'html.parser')

这个hello是自定义的变量,这个变量是个BS对象,通过使用BS对象的方法,进行下一步的数据提取。

4、搜索DOM树

DOM树是文档对象模型的意思,简单说就是html树形结构,在这个树形结构中搜索想要的内容。

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

这个函数同findAll( ),简单来说就是用于获取当前Tag下满足括号内条件的子tag,

可供填写的过滤参数较多,先从name说起,name是个活用参数,它可以是‘字符串’、‘列表’、‘正则表达式’、‘True’、‘函数’,首先是

A、name参数的用法


name--传字符串举例:


print hello.find_all('title')

输出:

[<title>The Dormouse's story</title>]

这个应该很容易理解,输出了所有标签名=name的tag。再举个例子:


for a in hello.find_all('a'):
    print a

输出:

<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>

说明:若获取到多个tag,返回的结果是列表,所以在这里我用for遍历输出会更好的展示结果

下一个是

name--传正则表达式举例:


for a in hello.find_all(re.compile('^b')):
    print a

输出:

<body>
<p class="title" name="dromouse"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>,
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
</body>
<b>The Dormouse's story</b>

再举一个例子:


for a in hello.find_all(re.compile('.*The')):
     print a


输出为空,因为没有标签的名称包含The这个字符,也就是说name参数只能是Tag名称,不能是字符串。

ok,我们继续,下一个是

name--传列表,举例:


for a in hello.find_all(['a','b']):
     print a

输出:

<b>The Dormouse's story</b>
<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>


说明:传列表作为name参数,该函数则会获取列表中任意一个或多个能匹配到tag名称的tag,同样返回结果是一个列表。


name--传 True,举例:


for a in hello.find_all(True):
    print a

输出结果是整个html内容,也就是说,True参数可以匹配任意tag,所以会传回整个html数据内容,但要注意,单纯的字符串tag对象不会被返回。

name--传函数,举例:


def has_class_no_id(tag):
    return tag.has_attr('class') and not tag.has_attr('id')
print hello.find_all(has_class_no_id)

输出:

<p class="title" name="dromouse"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>,
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>


说明:其实输出结果中的第2-5行是没有前面的空格的,我特意加上的,为了便于理解。通过看代码可以明白定义的函数的作用是返回有class属性但没有id属性的tag,但是在输出结果中貌似看到了有id属性的a标签结果,是吧。其实函数获取到的是符合条件的父标签p本身,下面的子标签有什么属性跟它无关,但是既然是子标签,所以在获取p的时候,会把p自己和其子标签、后代标签都获取到。


B、keyword参数的用法(关键词)


简单的说,这个参数就是去查找属性值对应的tag,属性有哪些?class,href,id,这些都是tag的属性。

ok,举例说明:

通过class属性查找:

for a in hello.find_all(class_='sister'):
    print a

输出:

<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>

说明:这儿的class我写的 'class_',这是个特殊用法,因为‘class’这个变量被python征用了,所以我们不能再用它。注意这儿的返回结果是列表


通过id属性查找:


for a in hello.find_all(id='link1'):
    print a


输出:

<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>


通过href属性查找:


for a in hello.find_all(href='http://example.com/elsie'):
    print a

输出:

<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>


特殊属性查找:

注意在html5中会出现data-xxx的属性(或者其他不可直接查询的属性),比如data-status='1',data-online='1234',这类属性是不支持直接属性查询的,但是可以间接,对,这时候就要用到的attrs参数了,现在,我修改一下html模板:


<p data-type='aaa' class="title" name="dromouse"><b>The Dormouse's story</b></p>


我给p标签添加一个data-type属性,然后通过属性来查询这个p标签:


for a in hello.find_all(attrs={'data-type':'aaa'}):
    print a

输出:
<p class="title" data-type="aaa" name="dromouse"><b>The Dormouse's story</b></p>



C、text参数的用法(文本)


前面我们已经通过标签名、属性查询tag了,剩下的也不剩什么了(一个标签里边能查的都用的差不多了),无非就是string了。

ok,举例吧:

for a in hello.find_all(text=['The',' Elsie ']):
    print a
print '---------'
for a in hello.find_all(text=re.compile('Lacie')):
    print a

输出:

  Elsie 
---------
Lacie


说明:text和name一样是个活用参数,也接受字符串、列表、True、正则。第一部分我们用的是列表,注意了,若是用列表作为text参数,findall方法将会进行严格匹配,即把列表内任意一个值拿去和tag内的string进行整体匹配,必须完全一致才能匹配成功(包括空格);第二部分是用正则,把正则作为text参数的话,那就是模糊匹配,主要看正则的写法。这里返回的结果是字符串,也算作是一种tag,叫叶子tag,它也有父tag。


D、limit参数的用法(数量限制)


for a in hello.find_all('a',limit=2):
    print a

输出:

<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>

这个也比较好理解,不作过多解释。


E、recursive 参数


for a in  hello.head.find_all('title',recursive=False):
    print a
print '----------'
for a in  hello.find_all('title',recursive=False):
    print a

输出:

<title>The Dormouse's story</title>
----------

说明:这个参数默认是 recursive=True,意思是允许搜索后代标签,也就是说搜索范围是当前tag本身以及其后代tag;若=False,那就是将范围限定在直接子标签了,看例子中第一部分,我们在head中的直接子tag中搜索名为'title’的tag,之所以能搜索到,是因为title标签就是head 的子标签;而例中的第二部分是在以整个html树为当前tag的直接子标签范围中搜索名为‘title’的子tag,那肯定不能搜索到,因为title标签是整个html树标签的后代标签,只有html标签才是它的直接子标签。(如不能理解,那就多调试一下,多试几个例子)


(2)find()函数用法同find_all一致,唯一的区别是它返回的结果类型是tag


print  hello.find('p')

输出:

<p class="title" data-type="aaa" name="dromouse"><b>The Dormouse's story</b></p>


说明:这个函数它不会遍历DOM树,只会获取到第一个匹配到的结果并返回,或许你会想什么时候会用到这个函数呢?在DOM树中head和body两个标签都只有一个,如果用find_all就不太合适,用find会恰当。


(3)find_parents()  find_parent()


print  hello.find('a'),'\n----'
a_tag=hello.find('a')
print a_tag.find_parent('p')

输出:

<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a> 
----
<p class="story">Once upon a time there were three little sisters; and their names were
<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>,
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>

上述代码第一部分是获取第一个a标签,第二部分代码是获取a标签的父亲标签。我们再举个例子解释find_parents():


print  hello.find(string = 'Lacie'),'\n----'
a_tag=hello.find(string = 'Lacie')
for a in  a_tag.find_parents('a'):
    print a


输出:

Lacie 
----
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>

说明:其实find_parent()是查找符合条件的当前tag 的父tag,如果加个s,find_parents(),那么程序会遍历DOM树,在当前tag的所有父辈tag中查找满足条件的tag并返回一个列表。


(4)find_next_siblings()  find_next_sibling()


这个就不举例了,find_next_siblings()是在当前tag的所有后面的兄弟tag中查找符合条件的tag并返回一个列表;find_next_sibling()是在当前tag的所有后面的兄弟tag中查找符合条件的第一个tag返回该tag。


(5)find_previous_siblings()  find_previous_sibling()


find_previous_siblings()是在当前tag的所有前面的兄弟tag中查找符合条件的tag并返回一个列表;find_previous_sibling()是在当前tag的所有前面的兄弟tag中查找符合条件的第一个tag返回该tag。


(6)find_all_next()  find_next()


find_all_next()是在当前tag的所有后面的tag中(忽略父子关系)查找符合条件的tag并返回一个列表;是在当前tag的所有后面的tag中(忽略父子关系)查找符合条件的第一个tag返回该tag。(DOM树中,字符串也算作tag,称叶子tag)


(7)find_all_previous() 和 find_previous()


find_all_previous()是当前tag的所有前面的tag中(忽略父子关系)查找符合条件的tag并返回一个列表; find_previous()是在当前tag的所有前面的tag中(忽略父子关系)查找符合条件的第一个tag返回该tag。(DOM树中,字符串也算作tag,称叶子tag)


到这里BS对象的find_xxx的函数方法介绍的差不多了,现在我想说的是还有一种更方便更简单的方法可以在DOM树中获取个性化数据。那就是:

5、CSS选择器


CSS选择器的写法很简单,筛选tag的时候写class的时候直接写 .class值,写tag名的时候直接写 tag名,写id的时候直接写 #id值,所用的方法是BS.select()。

话不多说,上例子,首先是根据

(1)、标签名筛选:


for a in  hello.select('a'):
    print a

输出:
<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>

说明:因为返回的是列表,所以用for进行遍历输出更直观。


(2)、class值筛选:


for a in  hello.select('.sister'):
    print a

输出同上


(3)、id值筛选:


for a in  hello.select('#link1'):
    print a

输出:

<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>


(4)、组合筛选:


for a in  hello.select('p #link1'):
    print a,'\n----'
for a in  hello.select('body p #link1'):
    print a


输出:

<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a> 
----
<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>

在实际环境中,我们更多的是用到组合筛选来获取我们想要的数据,上述代码中的第一部分是在DOM树中获取标签名为p的所有直接子标签X,子标签X必须满足id值等于link1的条件;第二部分是DOM树中获取标签名为body的子标签名为p的所有直接子标签X,子标签X必须满足id值等于link1的条件。

说明:或许上面的解释比较书面化,简言之,参数之间的空格就是用来表示父子关系,前面的父,后面的是子,不能是隔代关系。


那如果我们想获取指定Tag下的子tag,怎么写呢?


for a in  hello.select('p > a'):
    print a
print '\n----'
for a in  hello.select('body > p #link1'):
    print a

输出:

<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>

----
<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>


上述代码中的第一部分是获取p标签下的所有a标签,写法中的大于符号可加可不加,空格必须要有,第二部分是获取body标签的子标签p的子标签中id值等于link1的tag。


过滤条件在细一点,获取指定Tag下满足条件的子tag:


for a in  hello.select('p .sister'):
    print a
print '----'
for a in  hello.select('p a["class"="sister"]'):
    print a

输出:

<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
----
<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>


上述代码中两个部分运行的结果一致,只是写法不同;第一部分是获取p标签下满足class条件的子标签,第二部分是获取p标签下满足class条件的a标签。列表就是用来携带属性值的,可以是class,id,href等


(4)、获取tag中的值(属性值或文本):


print  hello.select('p .sister')[0].get('href')
print  hello.select('p .sister')[0].get('class')
print  hello.select('p .sister')[0].get('id')
print  hello.select('p .sister')[1].get_text()

输出:

http://example.com/elsie
[u'sister']
link1
Lacie


怎样,是不是比前面的find好用太多,而且功能更强,喜欢就点个赞吧!





  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值