sakura, cherry blossoms, spring-7091532.jpg

BeautifulSoup4 详细用法

一、 Beautiful Soup 安装

Beautiful Soup 提供一些简单的、Python 式的函数用来处理导航、搜索、修改分析树等功能。它是一个工具箱,通过解析文档为用户提供需要抓取的数据,因为简单,所以不需要多少代码就可以写出一个完整的应用程序。

Beautiful Soup 3 目前已经停止开发,推荐在现在的项目中使用 Beautiful Soup 4,不过它已经被移植到 BS4 了,也就是说导入时我们需要导入 bs4。

# 安装 Beautiful Soup 4
pip install beautifulsoup4

Beautiful Soup 支持 Python 标准库中的 HTML 解析器,还支持一些第三方的解析器,如果我们不安装则 Python 会使用默认的解析器,但 lxml 解析器更加强大,速度更快,推荐安装。

pip install lxml

以下是一些解析器的信息:

解析器使用方法优势劣势
Python标准库BeautifulSoup(markup, “html.parser”)Python的内置标准库
执行速度适中
文档容错能力强
Python 2.7.3 or 3.2.2 前 的版本中,文档容错能力差
lxml HTML 解析器BeautifulSoup(markup, “lxml”)速度快
文档容错能力强
需要安装C语言库
lxml XML 解析器BeautifulSoup(markup, [“lxml”, “xml”])
BeautifulSoup(markup, “xml”)
速度快
唯一支持XML的解析器
需要安装C语言库
html5libBeautifulSoup(markup, “html5lib”)最好的容错性
以浏览器的方式解析文档
生成HTML5格式的文档
速度慢

二、 创建 Beautiful Soup 对象

from bs4 import BeautifulSoup

html_doc = """
<html>
<head>
    <title>The Dormouse's story</title>
</head>
<body>
<p class="title aq">
    <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>
"""
# BeautifulSoup 会自动检测传入文件的编码格式然后转化为 Unicode 格式
soup = BeautifulSoup(html_doc, 'lxml')
# 输出第一个 title 标签
soup.title
# 输出第一个 title 标签的标签名称
soup.title.name
# 输出第一个 title 标签的包含内容
soup.title.string
# 输出第一个 title 标签的父标签的标签名称
soup.title.parent.name
# 输出第一个  p 标签
soup.p
# 输出第一个  p 标签的 class 属性内容
soup.p['class']
# 输出第一个  a 标签的  href 属性内容
soup.a['href']
# soup 的属性可以被添加、删除或修改,其属性操作方法与字典一样
# 修改第一个 a 标签的 href 属性为 http://www.baidu.com/
soup.a['href'] = 'http://www.baidu.com/'
# 给第一个 a 标签添加 name 属性
soup.a['name'] = u'百度'
# 删除第一个 a 标签的 class 属性
del soup.a['class']

三、 四大对象种类

Beautiful Soup 将复杂 HTML 文档转换成一个复杂的树形结构,每个节点都是 Python 对象,可以归纳为4种。

1. Tag

Tag 通俗点讲就是 HTML 中的一个个标签,利用 soup 加标签名的方式可以轻松地获取这些标签的内容,不过有一点是它查找的是在所有内容中的第一个符合要求的标签。

对于 Tag,它有两个重要的属性:name 和 attrs。

# soup 对象本身比较特殊,它的 name 即为标签本身的名称
soup.title.name
# 通过 Tag.attrs 返回字典结构的属性
soup.p.attrs

2. NavigableString

要想获取标签内部的文字用 .string 即可。

# 返回类型是一个 NavigableString
soup.title.string

3. BeautifulSoup

BeautifulSoup 对象表示的是一个文档的全部内容,大部分时候可以把它当作一个特殊 Tag 对象,可以分别获取它的类型,名称,以及属性。

soup.name
soup.attrs

4. Comment

Comment 对象是一个特殊类型的 NavigableString 对象,其实输出的内容仍然不包括注释符号,但是如果不好好处理它,可能会对我们的文本处理造成意想不到的麻烦。

# Tag 结果
<a class="sister" href="<http://example.com/elsie>" id="link1"><!-- Elsie --></a>
# soup.a.string 结果
Elsie
# 返回类型
<class 'bs4.element.Comment'>

a 标签里的内容实际上是注释,但是如果我们利用 .string 来输出它的内容会发现它已经把注释符号去掉了,所以这可能会给我们带来不必要的麻烦。

# 首先判断了类型是否为 Comment 类型然后再进行其他操作
if type(soup.a.string)==bs4.element.Comment:
    print soup.a.string

四、遍历文档树

1. 直接子节点

# Tag.contents: 以列表形式返回所有子节点
soup.head.contents
# 使用 [num] 的形式获得具体值
soup.head.contents[1]
# Tag.children: 生成器可用于循环访问
soup.head.children
# 遍历一下
for child in  soup.body.children:
    print(child)

2. 所有子孙节点

# Tag.descendants: 对所有 tag 的子孙节点进行递归循环,和 children类似需要遍历获取内容
for child in soup.descendants:
    print(child)

3. 节点内容

# Tag.String: Tag 只有一个 String 子节点是可以这么访问,否则返回 None
soup.head.string
# Tag.Strings: 生成器可用于循环访问

4. 多个内容

# Tag.strings: 获取多个内容不过需要遍历获取
for string in soup.strings:
    print(repr(string))
# Tag.stripped_strings: 获取多个内容不过需要遍历获取(去除多余空白内容)
for string in soup.stripped_strings:
    print(repr(string))

5. 父节点

# Tag.parent: 父节点
body = soup.title
body.parent
# Tag.parents: 父到根的所有节点
body.parents

6. 全部父节点

# Tag.parents: 父到根的所有节点
content = soup.head.title.string
for parent in  content.parents:
    print(parent.name)

7. 兄弟节点

# Tag.next_sibling: 兄弟节点可以理解为和本节点处在统一级的节点(空白或者换行也可以被视作一个节点)
# Tag.previous_sibling
soup.p.next_sibling

8. 全部兄弟节点

# Tag.next_siblings: 对当前节点的兄弟节点迭代输出
# Tag.previous_siblings
for sibling in soup.a.next_siblings:
    print(repr(sibling))

9. 前后节点

# Tag.next_element
# Tag.previous_element
# 与 .next_sibling .previous_sibling 不同,它并不是针对于兄弟节点而是在所有节点,不分层次
soup.head.next_element

10. 所有前后节点

# .next_elements 和 .previous_elements 的迭代器就可以向前或向后访问文档的解析内容,就好像文档正在被解析一样
for element in soup.head.next_elements:
    print(repr(element))

五、搜索文档树

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

1. name 参数

# name 参数可以查找所有名字为 name 的 tag,字符串对象会被自动忽略掉
# 第一个参数为Tag的名称,第二个参数为匹配的属性
# 字符串参数
soup.find_all('p', 'story')
# 正则表达式
# import re
for tag in soup.find_all(re.compile("^b")):
    print(tag.name)
    
# 列表:将与列表中任一元素匹配的内容返回
soup.find_all(['a', 'b'])
# True:可以匹配任何值,查找到所有的tag,但是不会返回字符串节点
for tag in soup.find_all(True):
    print(tag.name)
    
# 函数:如果没有合适过滤器,那么还可以定义一个方法,方法只接受一个元素参数

2. keyword 参数

# 如果一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当作指定名字tag的属性来搜索
# 如果包含一个名字为 id 的参数,Beautiful Soup 会搜索每个 tag 的 id 属性
soup.find_all(id='link2')
# 如果传入 href 参数,Beautiful Soup 会搜索每个 ta g的 href 属性
soup.find_all(href=re.compile("elsie"))
# 使用多个指定名字的参数可以同时过滤 tag 的多个属性
soup.find_all(href=re.compile("elsie"), id='link1')
# 想用 class 过滤,不过 class 是 python 的关键词,加个下划线就可以
soup.find_all("a", class_="sister")
# attrs 参数定义一个字典参数来搜索包含特殊属性的 tag
soup.find_all(attrs={"data-foo": "value"})

3. text 参数

# text 参数可以搜搜文档中的字符串内容,与 name 参数的可选值一样, text 参数接受字符串、正则表达式、列表、True
soup.find_all(text="Elsie")
soup.find_all(text=["Tillie", "Elsie", "Lacie"])
soup.find_all(text=re.compile("Dormouse"))

4. limit 参数

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

5. recursive 参数

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

find() 与 find_all() 方法唯一的区别是 find_all() 方法的返回结果是包含一个元素的列表,而 find() 方法直接返回结果。

# tag搜索
# 直接搜索名为tagname的tag 如:find('head')
find(tagname)
# 搜索在list中的tag,如: find(['head', 'body'])
find(list)
# 搜索在dict中的tag,如:find({'head':True, 'body':True})
find(dict)
# 搜索符合正则的tag, 如:find(re.compile('^p')) 搜索以p开头的tag
find(re.compile(''))
# 搜索函数返回结果为true的tag, 如:find(lambda name: if len(name) == 1) 搜索长度为1的tag
find(lambda)
# 搜索所有tag
find(True)
 
# attrs搜索
# 寻找id属性为xxx的
find(id='xxx')
# 寻找id属性符合正则且algin属性为xxx的
find(attrs={id=re.compile('xxx'), algin='xxx'})
# 寻找有id属性但是没有algin属性的
find(attrs={id=True, algin=None})

六、CSS选择器

1. 通过标签名查找

soup.select('title')

2. 通过类名查找

soup.select('.sister')

3. 通过 id 名查找

soup.select('#link1')

4. 组合查找

# 查找 p 标签中,id 等于 link1 的内容,二者需要用空格分开
# 空格分开
soup.select('p #link1')
# 子标签查找
soup.select('head > title')

5. 属性查找

soup.select('a[class="sister"]')
# 组合方式查找,不在同一节点的空格隔开,同一节点的不加空格
soup.select('p a[href="<http://example.com/elsie>"]')
# 返回的结果都是列表形式,可以遍历形式输出然后用 get_text() 方法来获取它的内容
soup.select('title')[0].get_text()
for title in soup.select('title'):
    print(title.get_text())

About The Author

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注

Scroll to Top