Beautiful Soup是一个可以从HTML或XML文件中提取数据的Python库。它能够实现惯用的文档导航,查找,修改文档的方式等功能。Beautiful Soup支持Python标准库中的HTML解析器,还支持一些第三方的解析器,其中一个是 lxml,另一个可供选择的解析器是纯Python实现的 html5lib,html5lib的解析方式与浏览器相同。下表介绍了Beautiful Soup主要的解析器,以及它们的优缺点:
解析器 | 使用方法 | 优势 | 劣势 |
---|---|---|---|
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语言库 |
html5lib | BeautifulSoup(markup, “html5lib”) | 最好的容错性、以浏览器的方式解析文档、生成HTML5格式的文档 | 速度慢、不依赖外部扩展 |
1.安装
Beautiful Soup可使用pip安装,如代码所示:
pip install bs4
2.创建BeautifulSoup对象
要使用BeautifulSoup库解析网页,首先需要创建BeautifulSoup对象,将一段文档传入BeautifulSoup的构造方法,就能得到一个文档的对象,可以传入一段字符串或一个文件句柄,如代码所示:
from bs4 import BeautifulSoup
soup = BeautifulSoup(open("index.html"))
soup = BeautifulSoup("<html>data</html>")
Beautiful Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种: Tag,NavigableString,BeautifulSoup,Comment。
(1)、Tag对象
Tag对象与XML或HTML原生文档中的tag相同,包括HTML标签还有其中包含的文本内容。例如"<title>北京二手房房源_北京二手房出售|买卖|交易信息</title>“或”<div class=“mainInfo”>4室2厅</div>"都是Beautiful Soup中的Tag对象。
通过Tag名称可以很方便的在文档树中获取需要的Tag对象,使用Tag名称查找的方法只能获得文档树中的第一个同名Tag对象,示例代码如下:
>>> # 导入BeautifulSoup
>>> from bs4 import BeautifulSoup
>>> # 创建BeautifulSoup对象
>>> soup = BeautifulSoup('<b class="boldest">Extremely bold</b>', 'lxml')
>>> print(soup.b)
<b class="boldest">Extremely bold</b>
Tag有很多方法和属性,其中最重要的属性: name和attributes。
每个tag都有自己的名字,通过 .name来获取,示例代码如下:
>>> # 创建Tag对象
>>> tag = soup.b
>>> print(tag.name)
b
一个tag可能有很多个属性。<b class=“boldest”>有一个 “class” 的属性,值为 “boldest”。tag的属性的操作方法与字典相同:
>>> print(tag['class'])
['boldest']
也可以直接"点"取属性,返回的是一个地点,属性为键,属性值为值,示例代码如下:
>>> print(tag.attrs)
{'class': ['boldest']}
(2)、NavigableString对象
NavigableString对象为包含在Tag对象中的文本字符串内容,可使用string方法获取,示例代码如下:
>>> # 导入BeautifulSoup
>>> from bs4 import BeautifulSoup
>>> html = '''
<div class="communityName"><i></i><span class="label">小区名称</span><a href="/xiaoqu/1111027379370/" target="_blank" class="info ">世纪星城</a><a href="#around" class="map">地图</a></div>
'''
>>> # 创建BeautifulSoup对象
>>> soup = BeautifulSoup(html, 'lxml')
>>> print(soup.a.string)
世纪星城
string只能获取该Tag对象中的直系文本内容,可以使用text属性和get_text()方法获取Tag对象中后代节点的所有文本内容,返回值为字符串,示例代码如下:
>>> # 导入BeautifulSoup
>>> from bs4 import BeautifulSoup
>>> html = '''
<div class="room"><div class="mainInfo">4室2厅</div><div class="subInfo">高楼层/共10层</div></div>
'''
>>> # 创建BeautifulSoup对象
>>> soup = BeautifulSoup(html, 'lxml')
# 使用text属性获取div节点下的所有文本内容
>>> print(soup.div.text)
4室2厅高楼层/共10层
# 使用get_text()方法获取div节点下的所有文本内容
>>> print(soup.div.get_text())
4室2厅高楼层/共10层
(3)、BeautifulSoup对象
BeautifulSoup对象表示的是一个文档的全部内容,大部分时候,可以把它当作 Tag对象。BeautifulSoup对象并不是真正的HTML或XML的tag,所以它没有name和attribute属性。但BeautifulSoup对象包含了一个值为 “[document]” 的特殊属性 .name属性,示例代码如下:
>>> # 导入BeautifulSoup
>>> from bs4 import BeautifulSoup
>>> html = '''
<div class="room"><div class="mainInfo">4室2厅</div><div class="subInfo">高楼层/共10层</div></div>
'''
>>> # 创建BeautifulSoup对象
>>> soup = BeautifulSoup(html, 'lxml')
>>> print(soup.name)
[document]
(4)、Comment对象
Tag,NavigableString,BeautifulSoup几乎覆盖了html和xml中的所有内容,但是还有一些特殊对象就文档的注释部分。Comment 对象是一个特殊类型的 NavigableString对象,但是当它出现在HTML文档中时,会使用特殊的格式输出,示例代码如下:
>>> # 导入BeautifulSoup
>>> from bs4 import BeautifulSoup
>>> markup = "<b><!--Hey, buddy. Want to buy a used parser?--></b>"
>>> soup = BeautifulSoup(markup, 'lxml')
>>> comment = soup.b.string
>>> print(comment)
Hey, buddy. Want to buy a used parser?
3.搜索特定节点并获取其中的链接及文本
BeautifulSoup库中定义了很多搜索方法,常用的有find方法、find_all方法,还可以通过CSS选择器进行搜索。find()方法返回直接的结果,find_all()方法返回一个包含结果的列表,基本语法如下:
find(name, attrs, recursive, string, **kwargs)
find_all(name, attrs, recursive, string, **kwargs)
参数说明:
使用BeautifulSoup库解析某网站二手房详情页中的信息,如代码3-7所示:
# 导包
import requests
from bs4 import BeautifulSoup
# 定义LianJiaSpider类
class LianJiaSpider:
# 定义初始化函数
def __init__(self):
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/'
'537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36'}
# 定义发送请求函数
def get_page(self, url):
response = requests.get(url=url, headers=self.headers).content.decode('utf-8')
soup = BeautifulSoup(response, 'lxml')
return soup
# 定义解析函数,解析详情页的url地址
def parse_url(self, soup):
li_list = soup.find_all('li', 'clear LOGVIEWDATA LOGCLICKDATA')
for li in li_list:
# 解析每套房屋标题中的链接
detail_url = li.find('a', 'noresultRecommend img LOGCLICKDATA').get('href')
# 向详情页链接发送请求
detail_soup = self.get_page(detail_url)
# 调用parse()函数解析详情页信息
self.parse(detail_soup)
# 定义详情页解析函数
def parse(self, detail_soup):
# 创建空子典保存解析出的房屋信息
item = {}
# 解析详情页房屋标题
item['title'] = detail_soup.find('h1', 'main').string
# 解析详情页房屋所在小区
item['community'] = detail_soup.find('a', 'info').string
# 解析详情页属性值为"mainInfo"的div标签
main_list = detail_soup.find_all('div', 'mainInfo')
# 解析详情页房屋户型
item['house_type'] = main_list[0].string if main_list[0] else None
# 解析详情页房屋面积
item['area'] = main_list[2].string if main_list[2] else None
# 解析详情页房屋单价
item['unit_price'] = detail_soup.select('.unitPrice>span')[0].text
# 解析详情页房屋总价
item['total_price'] = detail_soup.find('span', 'total').string
print(item)
# 定义运行函数
def run(self, url):
tree = self.get_page(url)
self.parse_url(tree)
if __name__ == '__main__':
# 调用类创建实例对象
spider = LianJiaSpider()
url = 'https://xx.xxxxxx.com/ershoufang/'
# 调用run函数运行程序
spider.run(url)
BeautifulSoup支持大部分的CSS选择器在Tag或BeautifulSoup对象的select()方法中传入字符串参数,即可使用CSS选择器的语法找到tag,示例如下:
>>> from bs4 import BeautifulSoup
>>> 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>
"""
>>> # 创建BeautifulSoup对象
>>> soup = BeautifulSoup(html_doc, 'html.parser')
>>> # 通过tag查找:
>>> soup.select("title")
[<title>The Dormouse's story</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">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.select("html head title")
[<title>The Dormouse's story</title>]
>>> # 找到某个tag标签下的直接子标签:
>>> soup.select("head > title")
[<title>The Dormouse's story</title>]
>>> soup.select("p > 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.select("p > a:nth-of-type(2)")
[<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
>>> soup.select("p > #link1")
[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]
>>> soup.select("body > a")
[]