爬虫 | Beautiful Soup 初识
本博客将学习用 Beautiful Soup 库来实现数据抓取。将会通过爬取世界大学校园排名的数据来讲解 Beautiful Soup 库的基础知识。它包括如何用 Beautiful Soup 库的解析器去解析页面内容、如何遍历和搜索标签树、如何提取出关键的数据并保存到列表或者字典里。
Beautiful Soup 简介
Beautiful Soup 库简单来说,就是一个可以从 HTML 或 XML 文件中提取数据的 Python 库。本次实验中,第一个例子是在 最好大学网上去爬取 2019 年软科类世界大学排名的数据。爬取的内容如下图所示:
获取 HTML 页面
我们要爬取上图中的三列数据,世界排名、学校名字和总分。首先我们用 Chrome 浏览器去访问这个页面。然后使用快捷键 F12
打开开发者工具来观察该页面中我们所要爬取数据的标签结构。
把鼠标移到要定位代码的数据上,右键鼠标 检查
就能定位到相应的代码,如下图所示。从下图可以看到一所大学的信息是存储在表格的一行里的。 我们需要获取每一行的第 1 列、第 2 列 和第 5 列的数据。
使用调试工具观察标签结构后,不难发现,我们想要获取的数据都是存储在表格中的,如下图所示。在本实验中最主要的任务就是要学会如何去遍历 tbody
标签的孩子标签,获取 tr
标签里 td
标签的数据。
确定好目标之后,接下来使用 requests.get()
来获取整个页面的内容。用 import requests
来导入 Requests 库。Requests 库简单来说,就是自动提交网络请求,爬取 HTML 页面。
获取世界大学排名的页面信息
import requests
r = requests.get("https://labfile.oss.aliyuncs.com/courses/2184/2019%E8%BD%AF%E7%A7%91%E4%B8%96%E7%95%8C%E5%A4%A7%E5%AD%A6%E5%AD%A6%E6%9C%AF%E6%8E%92%E5%90%8D.html") # get 请求指定的页面信息
# print(r.status_code) # 检查状态码是否正确,状态为 200,说明访问成功
# 200
使用 r.encoding
来查看该页面的编码形式,如果页面的编码不是 utf-8,我们需要转换编码形式,以保证获取的数据不会发生乱码。
# print(r.encoding) # 查看编码形式
# ISO-8859-1
r.encoding = r.apparent_encoding # 转换成 utf-8 的编码
# print(r.encoding)
# utf-8
使用 r.text
来获取整个页面内容,使用 r.headers
来获取页面的头信息。
html = r.text # 获取页面内容
# print(html)
解析 HTML 页面
Beautiful Soup 库有专门的 解析器。解析器的作用简单来说,就是用来解析和提取 HTML 或者 XML 页面的数据。它有四种解析器:
- Python标准库
- lxml HTML 解析器
- lxml XML 解析器
- html5lib
本次实验里使用的是 Python 标准库的解析器,只要安装了 Beautiful Soup 4,便可以使用,而另外三种解析器需要安装后才能使用。Python 标准库解析器就是 bs4 的 HTML 解析器,后续我们要用 bs4 去提取标签中的指定元素。
使用 from bs4 import BeautifulSoup
导入 BeautifulSoup 库。使用 BeautifulSoup(markup, "html.parser")
去解析页面。
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, "html.parser") # 解析世界大学排名的页面
# print(soup)
遍历和搜索文档树
解析 HTML 页面之后,我们就可以利用 Beautiful Soup 库中的函数去遍历标签树。所谓的标签树,就是指 HTML 的标签之间具有一种层次关系,而这种关系可以用树形图的结构来表示,从下图可以看出标签之间的层次结构。
根据上图标签之间的层次结构可以画出一个标签树,如下图所示。
每个标签都有一个名字,每个标签可以有 0 个或者多个属性,标签之间的字符串是非属性字符串或者注释,如下图所示。
我们的实验任务是要获取页面上的三类数据,分别是学校排名、学校名字和学校总分。所以,我们需要遍历标签树,搜索到这三类数据并保存到列表中。从上面的内容我们已经知道,所需要的数据是在 tbody
的子标签里,所以我们需要使用 find()
去遍历 tbody
标签的孩子标签。
# 遍历搜索 `tbody` 标签的孩子节点,返回的是一个迭代器
data = soup.find("tbody").children
# print(data)
# <list_iterator object at 0x0000023F5F413880>
上面的代码中,find()
是搜索文档树中的一种方法,它能够返回第一个匹配的元素。它的完整参数形式为:find( name , attrs , recursive , string , **kwargs )
。参数 name
是用标签名去检索字符串,参数 attrs
是用标签属性值去检索字符串,参数 recursive
是否对子孙全部检索,默认为 True
,参数 string
是检索标签中的非属性字符串。
.children
是标签下行遍历的一种属性。标签一共有三种遍历形式,分别是下行遍历、上行遍历和平行遍历。
下行遍历有以下三个属性:
.children
是循环遍历儿子节点.descendants
是循环遍历所有子孙节点.contents
是将该标签中所有儿子节点存入列表
上行遍历有以下两个属性:
.parent
是访问该节点的父节点标签.parents
是循环遍历所有先辈节点
平行遍历有以下四个属性:
.next_sibling
是返回下一个平行节点的标签.previous_sibling
是返回上一个平行节点的标签.next_siblings
是返回后面所有平行节点的标签.previous_siblings
是返回前面所有平行节点的标签
现在我们已经获取了 tbody
标签中所有孩子标签了,由于返回的是一个迭代器,你可以使用 list(data)
以列表的形式输出看一看获取到的数据结果。我们这里直接使用 for
语句去遍历 tr
标签。
# for tr in data:
# print(tr)
'''
# 一条数据
<tr class="alt"><td>1</td><td class="align-left">
<a href="World-University-Rankings/Harvard-University.html" target="_blank">哈佛大学</a></td>
<td><a href="World-University-Rankings-2019/USA.html" target="_blank" title="查看美国大学排名">
<img src="image/flag/USA.png"/></a></td><td class="hidden-xs">1</td>
<td>100.0</td>
<td class="hidden-xs need-hidden alumni">100.0</td>
<td class="hidden-xs need-hidden award" style="display:none;">100.0</td>
<td class="hidden-xs need-hidden hici" style="display:none;">100.0</td>
<td class="hidden-xs need-hidden ns" style="display:none;">100.0</td>
<td class="hidden-xs need-hidden pub" style="display:none;">100.0</td>
<td class="hidden-xs need-hidden pcp" style="display:none;">78.2</td>
</tr>
'''
运行结果后,可以看到每一对 tr
标签都被遍历了,但是我们需要的td
标签中的非属性字符串。这里使用 isinstance(tr, bs4.element.Tag)
去判断 tr
是否是 bs4 标签类型的元素。再用搜索文档树的另一种方法 find_all()
查询到所有td
标签并保存到列表 tds 里。
import bs4
info = [] # 定义一个列表去保存最后需要的数据
for tr in soup.find('tbody').children:
# 用 isinstance() 函数来判断一个对象是否是一个已知的类型
if isinstance(tr, bs4.element.Tag): # 判断 tr 是否是 bs4 标签类型的元素
tds = tr.find_all('td') # 把 tr 标签中的所有 td 标签的内容存储在列表 tds
# 把排名、大学名字、总分放入 info 列表
info.append([tds[0].string, tds[1].string, tds[4].string])
print(info)
# [['1', '哈佛大学', '100.0'], ['2', '斯坦福大学', '75.1'], ['3', '剑桥大学', '72.3'], ['4', '麻省理工学院', '69.0'], ['5', '加州大学-伯克利', '67.9'], ['6', '普林斯顿大学', '60.0'], ['7', '牛津大学', '59.7'], ['8', '哥伦比亚大学', '59.1'], ['9', '加州理工学院', '58.6'], ['10', '芝加哥大学', '55.1'], ['11', '加州大学-洛杉矶', '50.8'], ['11', '耶鲁大学', '50.8'], ['13', '康奈尔大学', '49.8'], ['14', '华盛顿大学-西雅图', '48.7'], ['15', '伦敦大学学院', '47.9'], ['16', '约翰霍普金斯大学', '47.6'], ['17', '宾夕法尼亚大学', '47.3'], ['18', '加州大学-圣地亚哥', '47.1'], ['19', '苏黎世联邦理工学院', '46.1'], ['20', '加州大学-旧金山', '42.2'], ['20', '密歇根大学-安娜堡', '42.2'], ['22', '华盛顿大学-圣路易斯', '41.7'], ['23', '帝国理工学院', '41.6'], ['24', '多伦多大学', '41.4'], ['25', '东京大学', '40.7'], ['26', '哥本哈根大学', '40.2'], ['27', '威斯康星大学-麦迪逊', '38.8'], ['28', '杜克大学', '38.6'], ['29', '西北大学(埃文斯顿)', '38.4'], ['30', '纽约大学', '38.1'], ['31', '爱丁堡大学', '37.4'], ['32', '京都大学', '37.0'], ['33', '曼彻斯特大学', '36.9'], ['33', '北卡罗来纳大学-教堂山', '36.9'], ['35', '洛克菲勒大学', '36.2'], ['35', '英属哥伦比亚大学', '36.2']...]
find_all()
它能够查询所有符合条件的元素,以列表的形式返回查询结果。它的完整参数形式与 find()
相同,这里就不在赘述了,若忘记,请回看以上内容。现在所有的 td
标签都保存在 tds
里面,我们用 tag.string
去取出标签中的非属性字符串。这是 Tag 标签的基本属性之一,称为 NavigableString
。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)