Beautifulsoup 库 -- 03 -- 搜索文档树

x33g5p2x  于2021-09-19 转载在 其他  
字(14.0k)|赞(0)|评价(0)|浏览(486)

1. 搜索文档树

  • 文档依旧是 Alice;
html_doc = """ <html><head><title>The Dormouse's story</title></head> <body> <p class="title"><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> """

from bs4 import BeautifulSoup
soup = BeautifulSoup(html_doc, 'html.parser')
  • Beautiful Soup 定义了很多搜索方法,这里着重介绍 2 个:

  • find();

  • find_all() ;

1.1 过滤器

  • 一共有 stringlistregular expressionTruefunction 五种类型的过滤器;
  • string 过滤器主要用于完全匹配属性值;
  • list 过滤器可以极其方便的查找多个值;
  • regular expression 过滤器可以用于不完全匹配等其他特殊匹配;
  • True 过滤器可以用来确定存在某些属性;
  • function 过滤器最为强大,尽管写起来比上述几个过滤器复杂,但是可以实现任何过滤;
    详细参考

1.1.1 string

  • 最简单的过滤器是字符串,在搜索方法中传入一个字符串参数,Beautiful Soup 会查找与字符串完整匹配的内容;
  • 下面的例子用于查找文档中所有的 <b> 标签:
soup = BeautifulSoup(html_doc, 'html.parser')
print(soup.find_all('b'))

输出:

[<b>The Dormouse's story</b>]
  • 如果传入字节码参数,Beautiful Soup 会当作 UTF-8 编码,可以传入一段 Unicode 编码来避免 Beautiful Soup 解析编码出错;

1.1.2 list

  • 如果传入列表参数,Beautiful Soup 会将与列表中任一元素匹配的内容返回。
  • 下面代码找到文档中所有 <a> 标签和 <b>标签:
soup = BeautifulSoup(html_doc, 'html.parser')
print(soup.find_all(["a", "b"]))

输出:

[<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>]

1.1.3 regular expression

  • 如果传入正则表达式作为参数,Beautiful Soup 会通过正则表达式的 search() 来匹配内容。
  • 下面例子中找出所有以 b 开头的标签,这表示 <body><b> 标签都应该被找到:
soup = BeautifulSoup(html_doc, 'html.parser')
for tag in soup.find_all(re.compile("^b")):
    print(tag.name)

输出:

body
b

1.1.4 True

  • True 可以匹配任何值,下面代码查找到所有的 tag,但是不会返回字符串节点;
soup = BeautifulSoup(html_doc, 'html.parser')
for tag in soup.find_all(True):
    print(tag.name)

输出:

html
head
title
body
p
b
p
a
a
a
p

1.1.5 function

  • 如果没有合适过滤器,那么还可以定义一个方法,方法只接受一个元素参数;
  • 如果这个方法返回 True 表示当前元素匹配并且被找到,如果不是则反回 False;
  • 下面方法校验了当前元素,如果包含 class 属性却不包含 id 属性,那么将返回 True:
soup = BeautifulSoup(html_doc, 'html.parser')

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

#将这个方法作为参数传入 find_all() 方法,将得到所有<p>标签:
print(soup.find_all(has_class_but_no_id))

输出:

[<p class="title"><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>]

一开始我对上述输出也有误解,明明是有 <a> 标签的,但是后来通过调试后发现,其实是因为 <a> 标签是在 <p> 标签中包含;

  • 通过一个方法来过滤某一类标签属性的时候,这个方法的参数是要被过滤掉的属性的值(即你不想看到的值), 而不是这个标签。
  • 下面的例子是找出 href 属性不符合指定正则的 a 标签:
soup = BeautifulSoup(html_doc, 'html.parser')

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

print(soup.find_all(href=not_lacie))

输出:

[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

1.2 find_all()

  • find_all( name , attrs , recursive , string , /*/*kwargs )
  • find_all() 方法搜索当前 tag 的所有 tag 子节点,并判断是否符合过滤器的条件。

1.2.1 name 参数

  • name 参数可以查找所有名字为 name 的 tag,字符串对象会被自动忽略掉。
  • soup.find_all("title")
  • 搜索 name 参数的值可以使任一类型的过滤器 ,字符串,正则表达式,列表,方法或是 True 。

1.2.2 keyword 参数

  • 如果一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当作指定名字 tag 的属性来搜索,如果包含一个名字为 id 的参数,Beautiful Soup 会搜索每个 tag 的 id 属性.
print(soup.find_all(id='link2'))
--- 输出 ---
[<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
  • 搜索指定名字的属性时可以使用的参数值包括:字符串 , 正则表达式 , 列表, True .

1.2.3 string 参数

  • 通过 string 参数可以搜搜文档中的字符串内容。
  • 与 name 参数的可选值一样,string 参数接受:字符串 , 正则表达式 , 列表, True。
soup = BeautifulSoup(html_doc, 'html.parser')

print(soup.find_all(string="Elsie"))
print(soup.find_all(string=["Tillie", "Elsie", "Lacie"]))
print(soup.find_all(string=re.compile("Dormouse")))

输出:

['Elsie']
['Elsie', 'Lacie', 'Tillie']
["The Dormouse's story", "The Dormouse's story"]
  • 虽然 string 参数用于搜索字符串,还可以与其它参数混合使用来过滤 tag.Beautiful Soup 会找到 .string 方法与 string 参数值相符的 tag;
  • 搜索内容里面包含“Elsie”的 <a>标签:
soup = BeautifulSoup(html_doc, 'html.parser')

print(soup.find_all("a", string="Elsie"))

输出:

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

1.2.4 limit 参数

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

print(soup.find_all("a", limit=2))

输出:

[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
  • 文档树中有 3 个 tag 符合搜索条件,但结果只返回了 2 个,因为我们限制了返回数量。

1.2.5 recursive 参数

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

1.3 像调用 find_all() 一样调用 tag

  • find_all() 几乎是 Beautiful Soup 中最常用的搜索方法,所以我们定义了它的简写方法。
  • BeautifulSoup 对象和 tag 对象可以被当作一个方法来使用,这个方法的执行结果与调用这个对象的 find_all() 方法相同:
soup = BeautifulSoup(html_doc, 'html.parser')

print(soup.find_all("a"))
print("----------------")
print(soup("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>]

1.4 find()

find( name , attrs , recursive , string , /*/*kwargs )

  • find_all() 方法将返回文档中符合条件的所有 tag。
  • 文档中只有一个 <body>标签,那么使用 find_all() 方法来查找 <body>标签就不太合适;
  • 使用 find_all 方法并设置 limit=1 参数不如直接使用 find() 方法:
soup = BeautifulSoup(html_doc, 'html.parser')

print(soup.find_all('title', limit=1))
print("--------")
print(soup.find('title'))

输出:

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

从上边看出,find_all() 方法的返回结果是值包含一个元素的列表,而 find() 方法直接返回结果。

  • find_all() 方法没有找到目标是返回空列表;
  • find() 方法找不到目标时,返回 None。
  • find_all() 和 find() 只搜索当前节点的所有子节点,孙子节点等。

1.5 find_parents() 和 find_parent()

  • find_parents() 和 find_parent() 用来搜索当前节点的父辈节点,搜索方法与普通 tag 的搜索方法相同,搜索文档搜索文档包含的内容;
  • find_parents( name , attrs , recursive , string , /*/*kwargs )
  • find_parent( name , attrs , recursive , string , /*/*kwargs )
soup = BeautifulSoup(html_doc, 'html.parser')

string_a = soup.find(string="Lacie")

print(string_a.find_parents("a"))
print("------------------")
print(string_a.find_parent("p"))

输出:

[<a class="sister" href="http://example.com/lacie" id="link2">Lacie</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>

1.6 find_next_siblings() 和 find_next_sibling()

  • find_next_siblings( name , attrs , recursive , string , /*/*kwargs )

  • 返回所有符合条件的后面的兄弟节点;

  • find_next_sibling( name , attrs , recursive , string , /*/*kwargs )

  • 只返回符合条件的后面的第一个 tag 节点;

soup = BeautifulSoup(html_doc, 'html.parser')

link_first = soup.a
print(link_first.find_next_siblings("a"))
print("_________________")
print(link_first.find_next_sibling("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/lacie" id="link2">Lacie</a>

1.7 find_previous_siblings() 和 find_previous_sibling()

  • find_previous_siblings( name , attrs , recursive , string , /*/*kwargs )

  • 返回所有符合条件的前面的兄弟节点;

  • find_previous_sibling( name , attrs , recursive , string , /*/*kwargs )

  • 返回第一个符合条件的前面的兄弟节点;

1.8 find_all_next() 和 find_next()

  • find_all_next( name , attrs , recursive , string , /*/*kwargs )

  • 返回所有符合条件的节点;

  • find_next( name , attrs , recursive , string , /*/*kwargs )

  • 返回第一个符合条件的节点;

1.9 find_all_previous() 和 find_previous()

  • find_all_previous( name , attrs , recursive , string , /*/*kwargs )

  • 返回所有符合条件的节点;

  • find_previous( name , attrs , recursive , string , /*/*kwargs )

  • 返回第一个符合条件的节点;

1.10 CSS 选择器

  • Beautiful Soup支持大部分的CSS选择器
  • 在 Tag 或 BeautifulSoup 对象的 .select() 方法中传入字符串参数,即可使用 CSS 选择器的语法找到tag。
soup = BeautifulSoup(html_doc, 'html.parser')

print(soup.select("title"))
print("-----")
print(soup.select("p:nth-of-type(3)"))

输出:

[<title>The Dormouse's story</title>]
-----
[<p class="story">...</p>]
  • 通过 tag 标签逐层查找:
soup = BeautifulSoup(html_doc, 'html.parser')

print(soup.select("body a"))
print("-----")
print(soup.select("html head title"))

输出:

[<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>]
-----
[<title>The Dormouse's story</title>]
  • 找到某个 tag 标签下的直接子标签:
soup = BeautifulSoup(html_doc, 'html.parser')

print(soup.select("head > title"))
print("-----")
print(soup.select("p > #link1"))

输出:

[<title>The Dormouse's story</title>]
-----
[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]
  • 找到兄弟节点标签:
soup = BeautifulSoup(html_doc, 'html.parser')

print(soup.select("#link1 ~ .sister"))
print("-----")
print(soup.select("#link1 + .sister"))

输出:

[<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/lacie" id="link2">Lacie</a>]
  • 通过 CSS 的类名查找:
soup = BeautifulSoup(html_doc, 'html.parser')

print(soup.select(".sister"))
print("-----")
print(soup.select("[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>]
-----
[<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 的 id 查找:
soup = BeautifulSoup(html_doc, 'html.parser')

print(soup.select("#link1"))
print("-----")
print(soup.select("a#link2"))

输出:

[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]
-----
[<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
  • 同时用多种 CSS 选择器查询元素:
soup = BeautifulSoup(html_doc, 'html.parser')

print(soup.select("#link1,#link2"))

输出:

[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
  • 通过是否存在某个属性来查找:
soup = BeautifulSoup(html_doc, 'html.parser')

print(soup.select('a[href]'))

输出:

[<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>]
  • 通过属性的值来查找:
soup = BeautifulSoup(html_doc, 'html.parser')

print(soup.select('a[href="http://example.com/elsie"]'))
print(soup.select('a[href^="http://example.com/"]'))

输出:

[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</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>]
  • 返回查找到的元素的第一个:
soup = BeautifulSoup(html_doc, 'html.parser')

print(soup.select_one(".sister"))

输出:

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

相关文章