跟着崔庆才学爬虫2:Beautiful Soup的使用(2)

方法选择器

前面讲的选择方法都是基于属性来选择的,这种方法快,但是进行一些比较复杂的选择时,会变得比较繁琐,不够灵活,好在Beautiful Soup还为我们提供了一些查询方法,例如find_all和find等,调用这些方法再传入相应的参数,就可以灵活查询了。

find_all

顾名思义就是查询所有符合条件的元素,可以给他传入一些属性或文本来得到符合条件的元素,功能非常强大,具体API如下:

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

name

我们可以根据name参数来查询元素,下面用实例来感受一下:

html='''
<div class="panel">
    <div class="panel-heading">
        <h4>Hello</h4>
    </div>
    <div class="panel-body">
        <ul class="list" id="list-1">
            <li class="element">Foo</li>
            <li class="element">Bar</li>
            <li class="element">Jay</li>
        </ul>
        <ul class="list list-small" id="list-2">
            <li class="element">Foo</li>
            <li class="element">Bar</li>            
        </ul>
    </div>
</div>
'''
from bs4 import BeautifulSoup
soup = BeautifulSoup(html,'lxml')
print(soup.find_all(name='ul'))
print(type(soup.find_all(name='ul')))

运行结果如下:[<ul class="list" id="list-1">
<li class="element">Foo</li>
<li class="element">Bar</li>
<li class="element">Jay</li>
</ul>, <ul class="list list-small" id="list-2">
<li class="element">Foo</li>
<li class="element">Bar</li>
</ul>]
<class 'bs4.element.ResultSet'>

 这里调用了find_all方法,向其中传入name参数,其参数值为ul,意思是查询所有ul节点。返回的结果是列表类型,长度为2,说明一共有两个ul节点,列表中的每个元素依然都是'bs4.element.ResultSet'类型

因为都是Tag类型,所以依然可以进行嵌套查询,下面我们就先查询所有ul节点,查出后再查询其内部的li节点

soup = BeautifulSoup(html,'lxml')
for ul in soup.find_all(name='ul'):
    print(ul.find_all(name='li'))

运行结果:[<li class="element">Foo</li>, <li class="element">Bar</li>, <li class="element">Jay</li>]
[<li class="element">Foo</li>, <li class="element">Bar</li>]

 返回的结果是列表类型,列表中的每个属性依旧是Tag类型。

接着我们就可以表里每个li节点,就可以得到他们的内容了。

for ul in soup.find_all(name='ul'):
    # print(ul.find_all(name='li'))
    for li in ul.find_all(name='li'):
        print(li.string)

运行结果:

Foo
Bar
Jay
Foo
Bar

attrs

除了根据节点名查询,我们也可以传入一些属性进行查询。下面用实例感受一下:

html='''
<div class="panel">
    <div class="panel-heading">
        <h4>Hello</h4>
    </div>
    <div class="panel-body">
        <ul class="list" id="list-1" name='element'>
            <li class="element">Foo</li>
            <li class="element">Bar</li>
            <li class="element">Jay</li>
        </ul>
        <ul class="list list-small" id="list-2">
            <li class="element">Foo</li>
            <li class="element">Bar</li>            
        </ul>
    </div>
</div>
'''
from bs4 import BeautifulSoup
soup = BeautifulSoup(html,'lxml')
print(soup.find_all(attrs={'id':'list-1'}))
print(soup.find_all(attrs={'name':'element'}))

运行结果如下:[<ul class="list" id="list-1" name="element">
<li class="element">Foo</li>
<li class="element">Bar</li>
<li class="element">Jay</li>
</ul>]
[<ul class="list" id="list-1" name="element">
<li class="element">Foo</li>
<li class="element">Bar</li>
<li class="element">Jay</li>
</ul>]

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

对于一些常用的属性,例如id和class等,我们可以不用attrs传递。例如要查询id为list-1的节点。可以直接传入id这个参数。继续使用上述文本我们看下实例:

from bs4 import BeautifulSoup
soup = BeautifulSoup(html,'lxml')
print(soup.find_all(id='list-1'))
print(soup.find_all(class_='element'))

运行结果如下:[<ul class="list" id="list-1" name="element">
<li class="element">Foo</li>
<li class="element">Bar</li>
<li class="element">Jay</li>
</ul>]
[<li class="element">Foo</li>, <li class="element">Bar</li>, <li class="element">Jay</li>, <li class="element">Foo</li>, <li class="element">Bar</li>]

 这里直接传入id='list-1',就可以查询id为list-1的节点元素了。而对于class来说,由于class在Python里是一个关键字,所以需要在后面加一个下划线_,即class_='element',返回结果依然是Tag对象组成的列表。

text

text参数可以用来匹配节点的文本,其传入的形式可以是字符串,也可以是正则表达式,示例如下:

html='''
<div class="panel">
    <div class="panel-body">
        <a>Hello, this is a link</a>
        <a>Hello, this is a link, too</a>
    </div>
</div>
'''
import re
from bs4 import BeautifulSoup
soup = BeautifulSoup(html,'lxml')
print(soup.find_all(text=re.compile('link')))

运行结果如下:['Hello, this is a link', 'Hello, this is a link, too']

 这里有两个a节点,其中内部包含文本信息,这里在find_all方法中传入text参数,该参数为正则表达式,返回结果是由所有与正则表达式相匹配的节点文本组成的列表。

find

除find_all方法外,还有find方法也可以查询符合条件的对象,只不过find返回的结果只有单个元素,也就是第一个匹配的元素,由于用法类似就不展开细说了。

css选择器

Beautiful Soup 还提供了另外一种选择器——css选择器,如果有学习或者前端工作的小伙伴那么肯定对css选择器不陌生。

使用css选择器,只需要调用select方法,传入相应的CSS选择器即可。我们用一个实例感受一下:

html='''<div class="panel">
    <div class="panel-heading">
        <h4>Hell0</h4>
    </div>
    <div class="panel-body">
        <ul class="list" id="list-1">
            <li class="element">Foo</li>
            <li class="element">Bar</li>
            <li class="element">Jay</li>
        </ul>
        <ul class="list list-samll" id="list-2">
            <li class="element">Foo</li>
            <li class="element">Bar</li>
        </ul>
    </div>
</div>'''
from bs4 import BeautifulSoup
soup=BeautifulSoup(html,'lxml')
print(soup.select('.panel .panel-heading'))
print(soup.select('ul li'))
print(type(soup.select('ul')[0]))

运行结果:

[<div class="panel-heading">
<h4>Hell0</h4>
</div>]
[<li class="element">Foo</li>, <li class="element">Bar</li>, <li class="element">Jay</li>, <li class="element">Foo</li>, <li class="element">Bar</li>]
<class 'bs4.element.Tag'>

 这里我们用了三次css选择器,返回结果均是由符合css选择器的节点组成的列表。例如,select('ul li')表示选择所有ul节点下面的所有li节点,结果便是所有li节点组成的列表。

最后一句中,我们打印输出了列表中元素的类型,可以看见依然是Tag类型。

嵌套选择

select方法同样支持嵌套选择。例如先选择所有ul节点,再遍历每个ul节点,选择其li节点,实例如下:

from bs4 import BeautifulSoup
soup=BeautifulSoup(html,'lxml')
for ul in soup.select('ul'):
    print(ul.select('li'))

运行结果:[<li class="element">Foo</li>, <li class="element">Bar</li>, <li class="element">Jay</li>]
[<li class="element">Foo</li>, <li class="element">Bar</li>]

 可以看到,正常输出了每个ul节点下的所有li节点组成的列表。

获取属性

既然知道节点是Tag类型。于是获取属性依然可以使用原来的方法。还是基于上面的HTML文本,这里尝试获取每个ul节点的id属性。

from bs4 import BeautifulSoup
soup=BeautifulSoup(html,'lxml')
for ul in soup.select('ul'):
    print(ul['id'])
    print(ul.attrs['id'])

运行结果:

list-1
list-1
list-2
list-2

可以看到,直接将属性名传入中括号和通过attrs属性获取属性值,都是可以成功获取属性的。

获取文本

要获取文本,可以使用之前前面说到的string属性,除此之外,还有一个方法,就是get_text,示例如下。

from bs4 import BeautifulSoup
soup=BeautifulSoup(html,'lxml')
for li in soup.select('li'):
    print('Get Text:',li.get_text())
    print('String:',li.string)

运行结果如下:

Get Text: Foo
String: Foo
Get Text: Bar
String: Bar
Get Text: Jay
String: Jay
Get Text: Foo
String: Foo
Get Text: Bar
String: Bar

 二者的效果是完全一致的,都可以获取节点的文本值。

总结

至此,Beautiful Soup的介绍基本就结束了,最后做一下简单的总结。

  • 推荐使用LXML解析库,必要时使用html.parser。
  • 节点选择器筛选能力弱,但是速度快。
  • 建议使用find,find_all方法查询匹配的单个结果或者多个结果。
  • 如果对CSS选择器熟悉,则可以使用select选择法
  • 25
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值