beautiful soup 4.0(bs4)搜索文档树(3)

1、概述

在上一篇文章中,主要介绍遍历文档树的方法。遍历文档树主要从文档的根节点开始,对文档进行逐一扫描。这是文档解析中一个非常重要的操作。除了这个操作之外,还有一个非常重要方式就是搜索文档树,搜索文档树并不是从根节点开始逐一的对文档进行操作,而是直接在整个文档中检索到我们需要节点以及内容。比如我们想获取一个文档的标题,可以直接索搜title标签,而不需要从本届点逐个进行遍历操作。

2、搜索文档树

搜索文档树的操作主要依靠以下两个方法来实现。

  • find
  • find_all

两个方法的使用方法基本相同,只是在数据返回上存在一定差别。find方法只返回符合条件的所有元素中的第一个,而find_all()方法会返回符合条件的所有元素。下面将详细介绍使用这两个方法搜索文档树的细节。

所谓细节就是可以使用哪些内容所谓搜索条件对整体文档树进行搜索。

2.1 过滤器

过滤器贯穿整个搜索的API.过滤器可以被用在tag的name中,节点的属性中,字符串中或他们的混合中。

2.1.1 字符串过滤器

最简单的过滤器是字符串.在搜索方法中传入一个字符串参数,Beautiful Soup会查找与字符串完整匹配的内容,下面的例子用于查找文档中所有的<p>标签,请参考如下代码:

for _p in soup.find_all('p'):
    print(_p.name)

2.1.2 正则表达式过滤器

如果传入正则表达式作为参数,Beautiful Soup会通过正则表达式的 match() 来匹配内容。如果我们想搜索文章中所有的H标签包含(H1-H6)可以使用此过滤器,请参考如下代码:

import re
for _p in soup.find_all(re.compile("^h[1-6]")):
    print(_p.name)

输出结果如下图所示:

2.1.3 列表

如果传入列表参数,Beautiful Soup会将与列表中任一元素匹配的内容返回.下面代码找到文档中所有<a>标签和<b>标签,请参考如下代码:

for _p in soup.find_all(['a','b']):
    print(_p.name)

2.1.4 True

True 可以匹配任何值,下面代码查找到所有的tag,但是不会返回字符串节点,请参考如下代码

for _p in soup.find_all(True):
    print(_p.name)

执行效果如下图所示:

2.1.5 使用自定义方法作为搜索条件

如果没有合适过滤器,那么还可以定义一个方法,方法只接受一个元素参数 [4] ,如果这个方法返回 True 表示当前元素匹配并且被找到,如果不是则反回 False。下面方法校验了当前元素,如果包含 class 属性却不包含 id 属性,那么将返回 True,请参考如下代码:

def has_class_but_no_id(tag):
    return tag.has_attr('class') and not tag.has_attr('id')

将这个方法作为参数传入 find_all() 方法,将得到所有包含class属性,但不包含id属性的标签,请参考如下代码:

soup.find_all(has_class_but_no_id)

输出效果如下图所示:

标签过滤方法可以使用复杂方法. 下面的例子可以过滤出前后都有文字的标签。

from bs4 import NavigableString
def surrounded_by_strings(tag):
    return (isinstance(tag.next_element, NavigableString)
            and isinstance(tag.previous_element, NavigableString))

for tag in soup.find_all(surrounded_by_strings):
    print(tag.name)

2.2 常用搜索函数

2.2.1 find_all()

find_all() 方法搜索当前tag的所有tag子节点,并判断是否符合过滤器的条件。该函数的语法结构图如下所示

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

1)参数说明

  • name 参数

该参数可以查找所有名字为指定值的元素,字符串对象会不再搜索范围之内。在上一节中大部分例子都是使用name参数来对标签的名称进行过滤。搜索 name 参数的值可以使任一类型的 过滤器 包括:字符窜,正则表达式,列表,方法或是 True。

  • keyword 参数

如果一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当作指定名字tag的属性来搜索,如果包含一个名字为 id 的参数,Beautiful Soup会搜索每个tag的”id”属性。

请参照如下代码:

soup.find_all(id='link2')
# [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]

如果传入 href 参数,Beautiful Soup会搜索每个tag的”href”属性,请参考如下代码:

soup.find_all(href=re.compile("elsie"))
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]

搜索指定名字的属性时可以使用的任何形式过滤器 .下面的例子在文档树中查找所有包含 id 属性的tag,无论 id 的值是什么:

soup.find_all(id=True)
# [<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的多个属性,请参照如下代码:

soup.find_all(href=re.compile("elsie"), id='link1')
# [<a class="sister" href="http://example.com/elsie" id="link1">three</a>]

按照CSS类名搜索tag的功能非常实用,但标识CSS类名的关键字 class 在Python中是保留字,使用 class 做参数会导致语法错误.从Beautiful Soup的4.1.1版本开始,可以通过 class_ 参数搜索有指定CSS类名的tag,请参照如下代码:

soup.find_all("a", class_="sister")
# [<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的 class 属性是 多值属性 .按照CSS类名搜索tag时,可以分别搜索tag中的每个CSS类名,请参照如下代码:

css_soup = BeautifulSoup('<p class="body strikeout"></p>')
css_soup.find_all("p", class_="strikeout")
# [<p class="body strikeout"></p>]

css_soup.find_all("p", class_="body")
# [<p class="body strikeout"></p>]
  • string参数

通过 string 参数可以搜搜文档中的字符串内容.与 name 参数的可选值一样, string 参数接受 任何形式的过滤器作为匹配条件.请参考如下代码:

soup.find_all(string="Elsie")
# [u'Elsie']

soup.find_all(string=["Tillie", "Elsie", "Lacie"])
# [u'Elsie', u'Lacie', u'Tillie']

soup.find_all(string=re.compile("Dormouse"))
[u"The Dormouse's story", u"The Dormouse's story"]

def is_the_only_string_within_a_tag(s):
    ""Return True if this string is the only child of its parent tag.""
    return (s == s.parent.string)

soup.find_all(string=is_the_only_string_within_a_tag)
# [u"The Dormouse's story", u"The Dormouse's story", u'Elsie', u'Lacie', u'Tillie', u'...']
  • limit参数

find_all() 方法返回全部的搜索结构,如果文档树很大那么搜索会很慢.如果我们不需要全部结果,可以使用 limit 参数限制返回结果的数量.效果与SQL中的limit关键字类似,当搜索到的结果数量达到 limit 的限制时,就停止搜索返回结果.

  • recursive 参数

调用tag的 find_all() 方法时,Beautiful Soup会检索当前tag的所有子孙节点,如果只想搜索tag的直接子节点,可以使用参数 recursive=False .

2.2.2 find()

find_all() 方法将返回文档中符合条件的所有tag,尽管有时候我们只想得到一个结果.比如文档中只有一个<body>标签,那么使用 find_all() 方法来查找<body>标签就不太合适, 使用 find_all 方法并设置 limit=1 参数不如直接使用 find() 方法.

2.2.3 其他方法

除了上面两个使用频率最高的方法外,bs4还提供了其他的一些方法。但是,这些方法仅仅是搜索方向与搜索范围不同。在使用的方法上与find和find_all是相同的,这些方法包括:

  1. find_parents() 和 find_parent()
  2. find_next_siblings() 和 find_next_sibling()
  3. find_previous_siblings() 和 find_previous_sibling()
  4. find_all_next() 和 find_next()
  5. find_all_previous() 和 find_previous()

猜你喜欢

转载自blog.csdn.net/amao1998/article/details/82660332