与lxml
同样,BeautifulSoup也是一个HTML/XML的解析器,主要功能也是如何解析和提取HTML/XML数据。html
几种解析工具的对比python
工具 | 速度 | 难度 | |
---|---|---|---|
正则表达式 | 最快 | 困难 | |
BeautifulSoup | 慢 | 最简单 | |
lxml | 快 | 简单 |
lxml 只会局部遍历,而Beautiful Soup 是基于HTML DOM的,会载入整个文档,解析整个DOM树,所以时间和内存开销都会大不少,因此性能要低于lxml。
安装
个人环境是Python 3.6.5,windows下cmd里执行pip安装便可。正则表达式
pip3 install beautifulsoup4
测试
python终端里导入beautifulsoup,无报错信息即安装成功。windows
>>from bs4 import BeautifulSoup >>
BeautifulSoup将复杂的HTML文档转换成一个复杂的树形结构,每一个节点都是Python对象,全部对象能够概括为4种:网络
BeautifulSoup
对象表示的是一个文档的内容。大部分时候,能够把它看成 Tag 对象,是一个特殊的 Tag。Comment
对象是一个特殊类型的 NavigableString 对象,其输出的内容不包括注释符号。工具
Tag能够简单理解为HTML文档中的一个个的标签,好比:性能
<head><title>The Dormouse's story</title></head> <ur><li class="item-0"><a href="link1.html">first item</a></li></ur>
上面HTML文档中的head、title、ur、li
都是HTML标签(节点名称),这些标签加上里面的内容就是tag。测试
获取Tagsui
# 导入模块 from bs4 import BeautifulSoup html = """ <html><head><title>The Dormouse's story</title></head> <body> <p class="title" name="dromouse"><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对象,指定lxml解析器 soup = BeautifulSoup(html, 'lxml') # prettify()方法格式化soup的内容 print(soup.prettify()) # soup.title选出title节点 print(soup.title) # <title>The Dormouse's story</title> print(type(soup.title)) # <class 'bs4.element.Tag'> print(soup.head) # <head><title>The Dormouse's story</title></head> print(soup.p) # <p class="title" name="dromouse"><b>The Dormouse's story</b></p>
说明:使用soup加节点名称能够获取节点内容,这些对象的类型是bs4.element.Tag
,可是它查找的是在内容中第一个符合要求的节点。好比上面代码有多个p标签,可是它只查找了第一个p标签。搜索引擎
对于Tag有两个重要的属性,name
和attrs
。当选择一个节点后,name属性获取节点的名称,attrs属性获取节点的属性(以字典形式返回)。
print(soup.name) # [document] #soup 对象自己比较特殊,它的 name 即为 [document] print(soup.head.name) # head #对于其余内部标签,输出的值便为标签自己的名称 print(soup.p.attrs) # {'class': ['title'], 'name': 'dromouse'} # 在这里,咱们把 p 标签的全部属性打印输出了出来,获得的类型是一个字典。 # 下面三种方法均可以获取字典里的值,是等价的,结果都同样 print(soup.p.get('class')) # ['title'] print(soup.p['class']) # ['title'] print(soup.p.attrs['class']) # ['title'] # 还能够针对属性或者内容进行修改 soup.p['class'] = "newClass" print (soup.p) # <p class="newClass" name="dromouse"><b>The Dormouse's story</b></p>
获取了Tag,也就是获取了节点内容,可是只想要获取节点内部的内容怎么办?只需使用.string
便可。
# 获取节点内容 print(soup.p.string) # The Dormouse's story print(type(soup.p.string)) # <class 'bs4.element.NavigableString'>
在选取节点的时候,也能够先选取一个节点,而后以这个节点为基准选取它的子节点,父节点,子孙节点等等,下面就介绍经常使用的选取方法。
.contents
tag的.contents
属性能够将tag的直接子节点以列表的方式输出。
下面例子选取head节点为基准,.contents
选取head的子节点title,而后以列表返回。
print(soup.head.contents) # [<title>The Dormouse's story</title>]
输出方式为列表,能够用列表索引来获取它的某一个元素.
print(soup.head.contents[0]) # <title>The Dormouse's story</title>
.children
children属性和contents属性不一样的是它返回的不是一个列表,而是一个生成器。可用for循环输出结果。
print(soup.head.children) # <list_iterator object at 0x0000017415655588> for i in soup.head.children: print(i) # <title>The Dormouse's story</title>
上面两个属性都只能获取到基准节点的下一个节点,要想获取节点的全部子孙节点,就可使用descendants
属性了。它返回的也是一个生成器。
print(soup.descendants) # <generator object descendants at 0x0000028FFB17C4C0>
还有其余属性如查找父节点,组父节点的属性就不记录了(平时不多用)。
BeautifulSoup提供了一些查询方法(find_all,find等),调用对应方法,输入查询参数就能够获得咱们想要的内容了,能够理解为搜索引擎的功能。(百度/谷歌=查询方法,查询内容=查询参数,返回的网页=想要的内容)
下面介绍最经常使用的find_all方法。
做用:查找全部
符合条件的元素,返回的是列表
形式
API:find_all(name, attrs, recursive, text, **kwargs)
1. name
name 参数能够根据节点名来查找元素。
A. 传字符串
最简单的过滤器是字符串.在搜索方法中传入一个字符串参数,BeautifulSoup会查找与字符串完整匹配的内容,下面的例子用于查找文档中全部的<p>标签
。
print(soup.find_all('p')) # 一般如下面方式写比较好 print(soup.find_all(name='p'))
B.传正则表达式
若是传入正则表达式做为参数,Beautiful Soup会经过正则表达式的 match() 来匹配内容.下面例子中找出全部以p
开头的标签。
import re print(soup.find_all(re.compile('^p')))
C.传列表
若是传入列表参数,BeautifulSoup会将与列表中任一元素匹配的内容返回。下面代码会找到HTML代码中的head标签和b标签。
print(soup.find_all(['head','b'])) # [<head><title>The Dormouse's story</title></head>, <b>The Dormouse's story</b>]
2. attrs
find_all中attrs参数能够根据节点属性查询。
查询时传入的参数是字典类型。好比查询id=link1的节点
print(soup.find_all(attrs={'id':'link1'})) # [<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>]
对于常见的属性,能够不用以attrs来传递,直接传入查询参数便可。好比id,class_(class为Python关键字,使用下划线区分),以下:
print(soup.find_all(id='link1')) print(soup.find_all(class_='sister'))
运行结果:
[<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>]
3. text
text 参数能够搜搜文档中的字符串内容,与 name 参数的可选值同样, text 参数接受 字符串 , 正则表达式 , 列表。下面代码查找节点里内容中有story字符串的节点,并返回节点的内容。
print(soup.find_all(text=re.compile('story'))) # ["The Dormouse's story", "The Dormouse's story"]
find方法与find_all方法的区别:
find_all:查询符合全部条件的元素,返回列表。
find:只查找第一个匹配到的元素,返回单个元素,类型tag。
查询方法与find_all大同小异。示例:
print(soup.find(name='p')) # 查询第一个p标签 print(soup.find(text=re.compile('story'))) # 查找第一个节点内容中有story字符串的节点内容
运行结果:
<p class="title" name="dromouse"><b>The Dormouse's story</b></p> The Dormouse's story
关于BeautifulSoup的使用就这样吧,经常使用我的就以为用好find_all便可(=.=~)
崔庆才 [Python3网络爬虫开发实战]:4.2-使用Beautiful Soup