在B站上学的一个小项目
用来新手爬虫可视化练练手挺适合
爬取站点【软科排名】2024年最新软科中国大学排名|中国最好大学排名 (shanghairanking.cn)
爬取内容:大学排名,大学名称,大学省份位置,大学类型,大学评分
如想要好的体验,进入我的博客
别总想着白嫖呀,点个赞行不
import requests
from bs4 import BeautifulSoup
import csv
import matplotlib.pyplot as plt
def getHTMLText(url):
try:
r = requests.get(url, timeout=30)#使用get请求url,且设置请求的超时时间为30秒
r.raise_for_status()#检查访问是否成功
r.encoding = r.apparent_encoding#根据响应内容的类型设置正确的编码,确保后续处理时不会出现编码错误
html = r.text#将响应的 HTML 内容(文本格式)赋值给变量 html
# 将html内容写入文件,检查爬取情况
filename = '测试html.text'
with open(filename, 'w', encoding='utf-8') as f:
f.write(html)
return html
except:
print("爬取失败")
return None
def fillUnivList(ulist, html):#这个函数作用是对爬取数据的筛选,建议按f12打开爬取网页元素构成以便于理解爬虫步骤
soup = BeautifulSoup(html, 'html.parser')#使用 BeautifulSoup 库解析传入的 HTML 文本,创建一个 soup 对象,方便后续进行 HTML 元素的查找。
table = soup.find('table', class_='rk-table')#在 soup 对象中查找第一个 <table> 标签,且其类名为 rk-table。将找到的表格赋值给变量 table。
#table: 这是我们要查找的 HTML 标签类型。在这里,我们在寻找一个 <table> 标签。
#class_='rk-table': 这是一个过滤条件,表示我们只想找到具有特定类名(rk-table)的 <table> 标签。class_ 是一个参数,用于指定 HTML 元素的类属性。
if table is None:
print("未找到排名表格")
return
tbody = table.find('tbody')#将tboy标签里的内容全部存入变量tbody中
#print(tbody) #检查数据内容用的,便于理解操作效果
if tbody is None:
print("未找到<tbody>标签")
return
data = tbody.find_all('tr')#在 <tbody> 中查找所有的 <tr> 标签(表格行),并将其赋值给变量 data。
#print(data) #测试 查看数据
for tr in data:
tds = tr.find_all('td')
if len(tds) < 5: # 爬取5行td的内容
continue
td_2 = tds[2].text.strip() if tds[2].text else ""#这边的if else判断tds[2]是否为空,如果为空就赋予引号的值
#总而言之:从列表tds中获取第三个元素的文本内容,并去掉该文本两端的空白字符。
td_3 = tds[3].text.strip() if tds[3].text else ""
#print(tds[1]) #观察里面的内容,并对其中的内容进行筛选
ulist.append([tds[0].string.strip(), tds[1].find('span').string.strip(),
td_2, td_3, tds[4].string.strip()])
#tds[1].find('span').string.strip() 的意思是:
# 从 tds 列表中获取第二个 <td> 元素,找到该元素内的第一个 <span> 标签,获取该标签中的文本内容,并去掉文本前后的空白字符。
def printUnivList(ulist, num):#将爬取的内容存入.csv文件
file_name = "大学排行.csv"
with open(file_name, 'w', newline='', encoding='utf-8') as f:#创建或覆盖一个名为 "大学排行.csv" 的文件,使用 UTF-8 编码以支持中文字符。
writer = csv.writer(f)#使用 csv.writer 创建一个写入器,并写入表头。
writer.writerow(["排名", "大学名称", "省市", "类型", "总分"])
for i in range(num):#写入数据并打印数据用于检查
u = ulist[i]
writer.writerow(u)
print(f"排名:{u[0]}\t大学名称:{u[1]}\t省市:{u[2]}\t类型:{u[3]}\t总分:{u[4]}")
# 折线图
def drawLineChart(ulist):
ranks = [(u[0]) for u in ulist]#ranks:从 ulist 中提取每个大学的排名
scores = [float(u[4]) for u in ulist]#scores:从 ulist 中提取每个大学的总分,并将其转换为浮点数
names = [u[1] for u in ulist]#names:从 ulist 中提取大学的名称
plt.rcParams['font.sans-serif'] = ['SimHei']#设置字体为 SimHei,以支持中文显示。
plt.figure(dpi=300) # 设置图像分辨率为300
plt.figure(figsize=(12, 8)) #设置图像的大小为12x8英寸
plt.plot(names, scores)#绘制以 ranks 为横坐标、scores 为纵坐标的折线图。
plt.xlabel('大学名称')
plt.ylabel('总分')
plt.title('大学排行榜折线图')
for i in range(len(names)):#在每个点上加上排名
plt.text(names[i], scores[i], ranks[i], ha='center', va='bottom', multialignment='center')
plt.xticks(rotation='vertical') # 将x轴的字体改为竖向显示
plt.savefig('大学排行榜折线图.pdf', format='pdf', bbox_inches='tight') # bbox_inches='tight' 可以去除多余的空白
plt.show()
# 饼图
def generatePieChart(ulist, num):
provinces = {}
for i in range(num):
province = ulist[i][2]
if province in provinces:
provinces[province] += 1
else:
provinces[province] = 1
labels = provinces.keys()
sizes = provinces.values()
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.figure(dpi=300) # 设置图像分辨率为300
plt.figure(figsize=(8, 6))
plt.pie(sizes, labels=labels, autopct='%1.1f%%')
plt.axis('equal')
plt.title('大学排行前30名的省份分布')
plt.show()
# plt.rcParams['font.sans-serif'] = ['SimHei']
# 柱形图
def printUnivList2(ulist, num):
ranks = []
scores = []
names = []
for i in range(num):
u = ulist[i]
ranks.append(int(u[0]))
scores.append(float(u[4]))
names.append(u[1])
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.figure(dpi=300) # 设置图像分辨率为300
plt.bar(ranks, scores)
plt.xlabel('排名')
plt.ylabel('总分')
plt.title('大学排名')
for i in range(len(ranks)):
# 在每个柱形图上方显示大学名称,并将其旋转45度
plt.text(ranks[i], scores[i], names[i], ha='center', va='bottom', fontsize=3, rotation=45)
plt.show()
# 散点图
def generateScatterPlot(ulist, num):
scores = [float(ulist[i][4]) for i in range(num)]
ranks = [i + 1 for i in range(num)]
names = [ulist[i][1] for i in range(num)]
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.figure(dpi=300) # 设置图像分辨率为300
plt.figure(figsize=(12, 8))
plt.scatter(ranks, scores)
plt.title('大学排名与总分的关系')
plt.xlabel('排名')
plt.ylabel('总分')
for i, name in enumerate(names):
plt.annotate(name, (ranks[i], scores[i]), xytext=(5, 5), textcoords='offset points', fontsize=8, rotation=45)
plt.show()
# 箱形图
def generateBoxPlot(ulist, num):
scores = [float(ulist[i][4]) for i in range(num)]
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.figure(dpi=300) # 设置图像分辨率为300
plt.figure(figsize=(8, 6))
plt.boxplot(scores)
plt.title('大学总分箱形图')
plt.ylabel('总分')
plt.show()
# 环形图
def generateDonutChart(ulist, num):
provinces = {}
for i in range(num):
province = ulist[i][2]
if province in provinces:
provinces[province] += 1
else:
provinces[province] = 1
labels = provinces.keys()
sizes = provinces.values()
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.figure(dpi=300) # 设置图像分辨率为300
plt.figure(figsize=(8, 6))
_, _, autotexts = plt.pie(sizes, labels=labels, autopct='%1.1f%%', wedgeprops=dict(width=0.8))
plt.setp(autotexts, size=8)
plt.title('大学排行前30名的省份分布(环形图)')
plt.axis('equal')
plt.show()
def main():
ulist = []
url = 'https://www.shanghairanking.cn/rankings/bcur/202411.html'
html = getHTMLText(url)
if html is not None:
fillUnivList(ulist, html)
printUnivList(ulist, 30)
printUnivList2(ulist, 30) # 柱形图
drawLineChart(ulist) # 折线图
generatePieChart(ulist, 30) # 饼图
generateScatterPlot(ulist, 30) # 调用生成散点图的函数
generateBoxPlot(ulist, 30) # 调用生成箱形图的函数
generateDonutChart(ulist, 30) # 调用生成环形图的函数
main()