BeautifulSoup文档学习3-搜索文档树

2020-03-13  本文已影响0人  JA_Cobra

搜索文档树

示例文档

>>> html_doc = """<html><head><title>睡鼠的故事</title></head>
<body>
<p class="title"><b>睡鼠的故事</b></p>

<p class="story">从前有三位小姐姐,她们的名字是:
<a href="http://example.com/elsie" class="sister" id="link1">埃尔西</a>,
<a href="http://example.com/lacie" class="sister" id="link2">莱斯</a>和
<a href="http://example.com/tillie" class="sister" id="link3">蒂尔莉</a>;
她们住在一个井底下面。</p>

<p class="story">...</p>
"""
>>> from bs4 import BeautifulSoup
>>> soup = BeautifulSoup(html_doc, 'html.parser')

几种过滤器

以下的过了长期贯穿了所有的搜索API函数,它们可以被使用在标签的名称、属性和文本这些上面

字符串

​ 最简单的过滤器就是字符串,在搜索方法中传入一个字符串参数,BeautifulSoup会查找与字符串匹配的内容,下面的例子用于查找文档中的所有的<b>标签:

>>> soup.find('b')
<b>睡鼠的故事</b>

​ 如果传入的是字节码参数,BeautifulSoup将会假设这个编码为UTF-8编码,为了避免出错,可以直接传入一个Unicode编码

正则表达式

​ 如果传入正则表达式作为参数,BeautifulSoup将会以正则表达式的match()方法来匹配内容。下面的示例中将找出所有以b开头的标签:

>>> import re
>>> for tag in soup.find_all(re.compile("^b")):
        print(tag.name)

body
b

列表

​ 如果传入列表参数,BeautifulSoup会将与列表中的任意元素进行匹配,返回匹配的内容。。下面的代码可以找到文档中所有的<a><b>标签:

>>> soup.find_all(["a", "b"])
[<b>睡鼠的故事</b>, <a class="sister" href="http://example.com/elsie" id="link1">埃尔西</a>, <a class="sister" href="http://example.com/lacie" id="link2">莱斯</a>, <a class="sister" href="http://example.com/tillie" id="link3">蒂尔莉</a>]

True

Ture值可以匹配任意值,下面代码查找所有的标签,但是不会返回字符串的节点:

>>> for tag in soup.find_all(True):
        print(tag.name)

html
head
title
body
p
b
p
a
a
a
p

函数

如果没有适合的过滤器,可以自己定义一个函数,**该函数只接受一个元素作为参数**。如果这个方法返回`True`表示当前元素匹配并且被找到,否则返回`False`

​ 下面这个函数用于匹配那些包含class属性但不包含id属性的标签:

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

​ 将这个函数作为参数传入find_all()方法,将得到所有的<p>标签:

>>> soup.find_all(has_class_but_no_id)
[<p class="title"><b>睡鼠的故事</b></p>, <p class="story">从前有三位小姐姐,她们的名字是:
<a class="sister" href="http://example.com/elsie" id="link1">埃尔西</a>,
<a class="sister" href="http://example.com/lacie" id="link2">莱斯</a>和
<a class="sister" href="http://example.com/tillie" id="link3">蒂尔莉</a>;
她们住在一个井底下面。</p>, <p class="story">...</p>]

​ 返回结果中只有<p>标签没有<a>标签(上面出现的<a>是包含在<p>中的),因为<a>标签中还定义了id,没有返回<html><head>,因为<html><head>没有class属性。

​ 如果传入一个函数来过滤一个像href这样的特定属性,传入函数的参数将式属性值,而不是整个标签。下面的函数可以找到所有拥有href属性但不包含lacie字符串的标签:

>>> def not_lacie(href):
        return href and not re.compile("lacie").search(href)

>>> soup.find_all(href=not_lacie)
[<a class="sister" href="http://example.com/elsie" id="link1">埃尔西</a>, <a class="sister" href="http://example.com/tillie" id="link3">蒂尔莉</a>]

find_all()

find_all(name, attrs, recrursive, string, limit, **kwargs)

find_all()方法搜索当前tag下的所有子节点,并判断是否符合过滤器的条件。

name参数

​ 通过name参数,可以指定名字来查找标签。最简单的用法如下:

>>> soup.find_all("title")
[<title>睡鼠的故事</title>]

​ 前面提过的过滤器的均可作为name参数的值:字符串、正则表达式、列表、函数或者一个布尔类型的值True

keyword参数

​ 如果一个指定名字的参数不是搜索内置的(name, attrs, recursive, string, limit)参数名,搜索时会把该参数当做指定tag的属性来搜索。

​ 比如传入一个名为id的参数,BeautifulSoup将会搜索每一个tag的id属性:

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

​ 如果传入一个名为href的参数,BeautifulSoup将会搜索每个tag的href属性:

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

​ 搜索指定名字的属性时可以使用的参数值包括:字符串、正则表达式、列表、函数和True

​ 下面的例子在文档树中查找所有包含id属性的tag,无论id是什么值都会匹配:

>>> soup.find_all(id=True)
[<a class="sister" href="http://example.com/elsie" id="link1">埃尔西</a>, <a class="sister" href="http://example.com/lacie" id="link2">莱斯</a>, <a class="sister" href="http://example.com/tillie" id="link3">蒂尔莉</a>]

注意:有些tag属性在搜索中不能使用,比如HTML5中的data-*属性。但是可以通过将这些属性放入一个字典中,然后传入attrs关键字参数来实现:

>>> data_soup = BeautifulSoup('<div data-foo="value">foo!</div>', "html.parser")
>>> data_soup.find_all(data-foo="value")
SyntaxError: keyword can't be an expression

>>> data_soup.find_all(attrs={"data-foo": "value"})
[<div data-foo="value">foo!</div>]

根据CSS进行搜索

​ 按照CSS类名搜索标签的功能非常实用,但由于表示CSS类名的关键字class在Python中是保留字,所以使用class作为参数会出错,在BS中可以通过class_参数搜索有指定CSS类名的标签:

>>> soup.find_all("a", class_="sister")
[<a class="sister" href="http://example.com/elsie" id="link1">埃尔西</a>, <a class="sister" href="http://example.com/lacie" id="link2">莱斯</a>, <a class="sister" href="http://example.com/tillie" id="link3">蒂尔莉</a>]

​ 根关键字参数一样,class_参数也支持不同类型的过滤器:字符串、正则表达式、函数或者True

>>> soup.find_all(class_=re.compile("itl"))
[<p class="title"><b>睡鼠的故事</b></p>]
>>> 
>>> def has_six_characters(css_class):
        return css_class is not None and len(css_class) == 6

>>> soup.find_all(class_=has_six_characters)
[<a class="sister" href="http://example.com/elsie" id="link1">埃尔西</a>, <a class="sister" href="http://example.com/lacie" id="link2">莱斯</a>, <a class="sister" href="http://example.com/tillie" id="link3">蒂尔莉</a>]

注意

string参数

​ 通过string参数可以搜索标签中的文本标签。与name参数一样,string参数接受字符串、正则表达式、列表、函数或者直接一个布尔类型值True

示例

>>> soup.find_all(string="埃尔西")
['埃尔西']
>>> 
>>> soup.find_all(string=["蒂尔莉", "埃尔西", "莱斯"])
['埃尔西', '莱斯', '蒂尔莉']
>>> 
>>> soup.find_all(string=re.compile("睡鼠"))
['睡鼠的故事', '睡鼠的故事']
>>> 
>>> def is_the_only_string_within_a_tag(s):
        """如果字符串是其父标签的唯一子节点,则返回 True。"""
        return (s == s.parent.string)

>>> soup.find_all(string=is_the_only_string_within_a_tag)
['睡鼠的故事', '睡鼠的故事', '埃尔西', '莱斯', '蒂尔莉', '...']

​ 尽管string参数是用于搜索字符串的,但是可以与其他的参数混合起来使用,下面的代码BS会找到所有与string参数值相匹配的<a>标签:

>>> soup.find_all("a", string="埃尔西")
[<a class="sister" href="http://example.com/elsie" id="link1">埃尔西</a>]

limit 参数

find_all()方法返回匹配过滤器的所有标签和文本,如果文档树很大,搜索就会变得很慢,如果不想要全部的结果,可以使用limit参数进行限制返回结果的数量,当搜索到的结果数量达到了limit的限制时,就停止搜索并返回结果。

​ 文档树中有3个标签符合搜索结果,但是结果只返回2个,因为设置了limit参数:

>>> soup.find_all("a", limit=2)
[<a class="sister" href="http://example.com/elsie" id="link1">埃尔西</a>, <a class="sister" href="http://example.com/lacie" id="link2">莱斯</a>]

recursive 参数

​ 如果调用mytag.find_all()方法,BS将会获取mytag的所有子孙节点,但是如果只是想搜索mytag的直接子节点,可以使用recursive=False参数,对比一下:

>>> soup.html.find_all("title")
[<title>睡鼠的故事</title>]
>>> 
>>> soup.html.find_all("title", recursive=False)
[]

像调用find_all()一样调用一个标签

​ 由于 find_all()几乎是 BeautifulSoup 中最常用的搜索方法,所以我们为它定义了一种简写的形式:如果你将 BeautifulSoup对象或 Tag 对象当作一个方法来使用,那么这个方法的执行结果与调用这个对象的 find_all()方法是相同的。

示例

以下的代码是等价的:

soup.find_all("a")
soup("a")

以下的代码也是等价的:

soup.title.find_all(text=True)
soup.title(text=True)

find() 方法

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

find_all()方法返回结果是一个列表,而find()方法是直接返回结果。find_all()方法没有找到目标返回的是空列表,find()方法没有找到就返回None。

CSS选择器

BeautifulSoup中有一个select()方法,该方法使用SoupSieve对解析的文档运行CSS选择器并返回所有匹配的元素。

​ 可以使用CSS选择器的语法找到Tag:

>>> soup.select("title")
[<title>睡鼠的故事</title>]
 
>>> soup.select("p:nth-of-type(3)")
[<p class="story">...</p>]

​ 通过tag标签逐层查找:

>>> soup.select("body a")
[<a class="sister" href="http://example.com/elsie" id="link1">埃尔西</a>, <a class="sister" href="http://example.com/lacie" id="link2">莱斯</a>, <a class="sister" href="http://example.com/tillie" id="link3">蒂尔莉</a>]
>>> soup.select("html head title")
[<title>睡鼠的故事</title>]

​ 找到某个tag标签下的直接字标签:

>>> soup.select("head > title")
[<title>睡鼠的故事</title>]
 
>>> soup.select("p > a")
[<a class="sister" href="http://example.com/elsie" id="link1">埃尔西</a>, <a class="sister" href="http://example.com/lacie" id="link2">莱斯</a>, <a class="sister" href="http://example.com/tillie" id="link3">蒂尔莉</a>]
>>> soup.select("p > a:nth-of-type(2)")
[<a class="sister" href="http://example.com/lacie" id="link2">莱斯</a>]
>>> soup.select("p > #link1")
[<a class="sister" href="http://example.com/elsie" id="link1">埃尔西</a>]
>>> soup.select("body > a")
[]

​ 找到兄弟节点:

>>> soup.select("#link1 ~ .sister")
[<a class="sister" href="http://example.com/lacie" id="link2">莱斯</a>, <a class="sister" href="http://example.com/tillie" id="link3">蒂尔莉</a>]
>>> soup.select("#link1 + .sister")
[<a class="sister" href="http://example.com/lacie" id="link2">莱斯</a>]

​ 通过CSS的类名查找:

>>> soup.select(".sister")
[<a class="sister" href="http://example.com/elsie" id="link1">埃尔西</a>, <a class="sister" href="http://example.com/lacie" id="link2">莱斯</a>, <a class="sister" href="http://example.com/tillie" id="link3">蒂尔莉</a>]
>>> soup.select("[class~=sister]")
[<a class="sister" href="http://example.com/elsie" id="link1">埃尔西</a>, <a class="sister" href="http://example.com/lacie" id="link2">莱斯</a>, <a class="sister" href="http://example.com/tillie" id="link3">蒂尔莉</a>]

​ 通过tag的ID查找:

>>> soup.select("#link1")
[<a class="sister" href="http://example.com/elsie" id="link1">埃尔西</a>]
>>> soup.select("a#link2")
[<a class="sister" href="http://example.com/lacie" id="link2">莱斯</a>]

​ 查找与选择器列表中的任何选择器匹配的tag:

>>> soup.select("#link1,#link2")
[<a class="sister" href="http://example.com/elsie" id="link1">埃尔西</a>, <a class="sister" href="http://example.com/lacie" id="link2">莱斯</a>]
上一篇下一篇

猜你喜欢

热点阅读