1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
| 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()
|