爬取中国大学排行
一、选题的背景(10 分)
随着时代的发展,大家越来越重视学习,高考结束后的学生们更加关注学校情况,以至于大家越来越关心大学的质量。一所好大学,对人生的影响是非常巨大的,作为判断标准的数据,在以往是需要花费大量时间是查找与翻阅的。通过爬虫中国大学排行,可以更加的便利大家,籍由此让大家了解一下近些年中国大学实力排行情况。来分析各个大学的情况,以便让大家更加直观的感受。分析排行较靠前的学校类型,以及学校分布情况等等。
二、主题式网络爬虫设计方案(10 分)
1.主题式网络爬虫名称
爬取中国大学排行
2.主题式网络爬虫爬取的内容与数据特征分析
主要是爬取中国大学排行网是软科,进行排行实时追踪,主要是通过requests 以及BeautfulSoup,从而获取具体数据。
3.主题式网络爬虫设计方案概述(包括实现思路与技术难点)
分析主题页面结构,获得爬取位置,再根据不同的爬取方法将数据保存,接着通过保存的数据进行数据分析以及数据可视化,最后使数据持久化,更方便未来的数据波动。
技术难点:数据分析,文件内有些数据无法进行比较,需进行内容删除,后才可以比较。数据持久化较难全面进行。
三、主题页面的结构特征分析(10 分)
1.主题页面的结构与特征分析
发现所有信息都被写在tbody标签下的tr中,每个tr表示一个学校,tr下的每个td表示一个具体信息。注意第一个tr中的信息表示格索引头,以此进行爬虫。
2.Htmls 页面解析
3.节点(标签)查找方法与遍历方法
节点有data rankings univnamecn score等等
四、网络爬虫程序设计(60 分)
1.数据爬取与采集
#获取html网页 url = 'https://www.shanghairanking.cn/rankings/bcur/2021' header={ 'user-agent':' Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36 Edg/96.0.1054.62'} #1 #数据爬取与采集 import requests from bs4 import BeautifulSoup url="https://www.shanghairanking.cn/rankings/bcur/2021" res=requests.get(url) res.encoding=res.apparent_encoding soup=BeautifulSoup(res.text) m,k=(1,4) td_list=soup.select('tbody tr td ') print('排 名 一一一一学校名称一一一一 --总 分--- '.replace("一",' ').replace("-",' ')) for i in soup.select('tbody a.name-cn'):#循环找所有a标签 print(("{:-^5} {:一^12} {:-^} ".format(m,i.string,td_list[k].string.strip())).replace("一",' ').replace("-",' ')) k+=6 m+=1 #对数据JSON化,爬取目标数据 import requests import json #用requests抓取网页信息 def getHTMLText(url): try: r = requests.get(url, timeout=30) #如果状态不是200,,就会引发HTTPError异常 r.raise_for_status() r.encoding = r.apparent_encoding return r.text except: return "产生异常" if __name__ == '__main__': #填入要请求的服务器地址 urls="https://www.shanghairanking.cn/api/pub/v1/bcur?bcur_type=11&year=2021" html=getHTMLText(urls) data = json.loads(html) print('排 名 一一一一学校名称一一一一 --总 分--- 类型'.replace("一",' ').replace("-",' ')) k=0 for i in data['data']['rankings']: print(("{:-^5} {:一^12} {:-^10} {:-^2}".format(i['ranking'],i['univNameCn'],i['score'],i['univCategory'] )).replace("一",' ').replace("-",' ')) k+=1 if k==30: break #保存数据,生成xlsx def saveHTMLText(lst): try: #对df数据类型中的columns赋值 headers = ['排名','学校名称','总分','类型'] #对df数据类型中的index赋值 index = [i for i in range(1,len(lst)+1)] #将数据列表转换为DataFrame对象 df = pd.DataFrame(lst,columns=headers,index=index) #判断磁盘里是否存在目标文件夹 if not os.path.exists('D:/asd.xlsx'): #不存在,则创建该文件夹 os.makedirs('D:/asd.xlsx') #生成xlsx文件 df.to_excel('D:/asd.xlsx') else: df.to_excel('D:/asd.xlsx') print("保存成功") #返回成功提示 except: print("保存失败") #返回失败提示
结果如下:
2.对数据进行清洗和处理
#读取文件并浏览 import pandas as pd df=pd.read_excel('D:/asd.xlsx') df.head() #删除无效列 df.drop('省市',axis=1,inplace=True) df.head() #重复值处理 df.duplicated() #空值处理 df['学校名称'].isnull() #异常值处理 df.describe()
结果如下:
3.文本分析(可选):jieba 分词、wordcloud 的分词可视化
#使用jieba分词 file=open('D:/asd.csv',encoding='utf_8') user_dict=file.read() print(user_dict) #读取文件,用jieba进行文本分词并保存文件中 asd_excel=open('D:/asd.csv','r',encoding='utf_8').read() #读取文件,编码格式utf-8,防止处理中文出错 import jieba.posseg as psg asd_words_with_attr=[(x.word,x.flag)for x in psg.cut(asd_excel) if len(x.word)>=2] #x.word为词本身,x.flag为词性 print(len(asd_words_with_attr))#输出 with open('cut_words.txt','w+')as f: for x in asd_words_with_attr: f.write('{0}\t{1}\n'.format(x[0],x[1])) #从cut_words.txt中读取带词性的分词结果列表 asd_words_with_attr=[] with open('cut_words.txt','w+')as f: for x in f.readlines(): pair=x.split() asd_words_with_attr.append((pair[0],pair[1])) #stop_attr中存放要过滤掉的词性列表 stop_attr=['vn','n'] #过滤清洗数据,将结果存放在words中 words=[x[0]for x in asd_words_with_attr if x[1] not in stop_attr]
结果如下:
4.数据分析与可视化(例如:数据柱形图、直方图、散点图、盒图、分布图)
import pandas as pd #数据加载与数据集统计信息查看 #使用pd.read_excel()的方法,将本地的asd.xlsx数据加载到DataFrame中 asd_df=pd.read_excel(r'D:/asd.xlsx') asd_df.head() #使用describe()方法查看数据表中的个数值字段的统计信息,了解数据集大致特征 asd_df.describe() import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns asd=pd.read_excel(r'D:/asd.xlsx') #数据可视化 #直方图 sns.distplot(asd['总得分'],label='asd') plt.grid() plt.legend(loc='upper right') #散点图 # 用来正常显示中文标签 plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示负号 plt.rcParams['axes.unicode_minus'] = False #x,y轴 plt.xlabel("2021排名") plt.ylabel("总得分") sns.regplot(x='2021排名',y='总得分',data=asd,color='r') # kind='hex' sns.jointplot(x="排名",y="热度",data=rank,kind='hex') # kind='kde' sns.jointplot(x="排名",y="热度",data=rank,kind="kde",space=0,color='g') #盒图 plt.xlabel("2021排名") plt.ylabel("总得分") sns.boxplot(x='2021排名',y='总得分',data=asd) #绘制饼图 #前30所学校 plt.figure(figsize=(12,8),dpi=80) # 各部分标签 label_list = ["综合", "理工", "师范"] # 各部分大小 size = [18, 10, 2] color = ["red", "green", "blue"] # 各部分突出值 explode = [0.05, 0, 0] patches, l_text, p_text = plt.pie(size, explode=explode, colors=color,\ labels=label_list, labeldistance=1.1,\ autopct="%1.1f%%", shadow=False, startangle=90, pctdistance=0.6) # 设置横轴和纵轴大小相等,这样饼才是圆的 plt.axis("equal") plt.legend() plt.show() #柱状图 sns.countplot(asd['学校类型']) sns.countplot(asd['省市']) #回归图 fig,axes=plt.subplots(2,2) #1.默认绘图效果 sns.regplot(x='2021排名',y='总得分',data=asd,ax=axes[0][0]) #2.ci参数可以控制是否显示置信区间 sns.regplot(x='2021排名',y='总得分',data=asd,ci=None,ax=axes[0][1]) #3.mark参数可以设置数据点格式,color参数可以设置颜色 sns.regplot(x='2021排名',y='总得分',data=asd,color='g',marker='*',ax=axes[1][0]) #4.fit_reg参数可以控制是否显示拟合的直线 sns.regplot(x='2021排名',y='总得分',data=asd,color='g',marker='+',fit_reg=False,ax=axes[1][1]) #选择排名以及总得分两个特征变量,绘制分布图 import pandas as pd import matplotlib.pyplot as plt colnames=["排名","总得分"] df=pd.read_excel('D:/asd.xlsx',skiprows=1,names=colnames) X = df.排名 Y = df.总得分 def A(): plt.scatter(X,Y,color="blue",linewidth=2) plt.title("总情况",color="blue") plt.grid() plt.show() def B(): plt.scatter(X,Y,color="green",linewidth=2) plt.title("总情况",color="blue") plt.grid() plt.show() def func(p,x): a,b,c=p return a*x*x+b*x+c def error(p,x,y): return func(p,x)-y def main(): plt.figure(figsize=(10,6)) p0=[0,0,0] Para = leastsq(error,p0,args=(X,Y)) a,b,c=Para[0] print("a=",a,"b=",b,"c=",c) plt.scatter(X,Y,color="blue",linewidth=2) x=np.linspace(0,20,20) y=a*x*x+b*x+c plt.plot(x,y,color="blue",linewidth=2,) plt.title("总情况") plt.grid() plt.show() print(A()) print(B()) print(main())
结果如下:
5.根据数据之间的关系,分析两个变量之间的相关系数,画出散点图,并建立变量之间的回归方程(一元或多元)。
#计算回归系数与截距 import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns plt.figure from sklearn.linear_model import LinearRegression asd=pd.read_excel('D:/asd.xlsx') pt=LinearRegression() pt.fit(asd[['2021排名']],asd['总得分']) print("回归系数为:",pt.coef_) print("截距:",pt.intercept_) #建立变量之间的回归方程 a=float(pt.coef_) b=float(pt.intercept_) print('y={:.2f}x+{:.2f}'.format(a,b)) #当x=20时 y=a*20+b print(y) print('当x=20,总得分:',y) #画出散点图 import seaborn as sns sns.regplot(x='2021排名',y='总得分',data=asd).format(a,b)) #调用LinearRegression的predict方法可以进行预测 predict_model.predict(X)[0:10]
结果如下:
6.数据持久化
import pymysql if __name__ == "__main__": args = dict( host = 'localhost', user = 'root', passwd = '...your password', db = 'pydb', charset = 'utf8' ) conn = pymysql.connect(**args) cursor = conn.cursor() cursor.execute("drop table if exists test") cursor.execute("create table test(id int,name varchar(20))") cursor.execute('insert into test values(1," ")') cursor.execute('insert into test values(2," ")') cursor.execute('insert into test values(3," ")') conn.commit() conn.close()
7.将以上各部分的代码汇总,附上完整程序代码
1 #获取html网页 2 url = 'https://www.shanghairanking.cn/rankings/bcur/2021' 3 header={ 4 'user-agent':' Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36 Edg/96.0.1054.62'} 5 #1 6 #数据爬取与采集 7 import requests 8 from bs4 import BeautifulSoup 9 url="https://www.shanghairanking.cn/rankings/bcur/2021" 10 res=requests.get(url) 11 res.encoding=res.apparent_encoding 12 soup=BeautifulSoup(res.text) 13 m,k=(1,4) 14 td_list=soup.select('tbody tr td ') 15 print('排 名 一一一一学校名称一一一一 --总 分--- '.replace("一",' ').replace("-",' ')) 16 for i in soup.select('tbody a.name-cn'):#循环找所有a标签 17 print(("{:-^5} {:一^12} {:-^} ".format(m,i.string,td_list[k].string.strip())).replace("一",' ').replace("-",' ')) 18 k+=6 19 m+=1 20 #对数据JSON化,爬取目标数据 21 import requests 22 import json 23 #用requests抓取网页信息 24 def getHTMLText(url): 25 try: 26 r = requests.get(url, timeout=30) 27 #如果状态不是200,,就会引发HTTPError异常 28 r.raise_for_status() 29 r.encoding = r.apparent_encoding 30 return r.text 31 except: 32 return "产生异常" 33 if __name__ == '__main__': 34 #填入要请求的服务器地址 35 urls="https://www.shanghairanking.cn/api/pub/v1/bcur?bcur_type=11&year=2021" 36 html=getHTMLText(urls) 37 data = json.loads(html) 38 print('排 名 一一一一学校名称一一一一 --总 分--- 类型'.replace("一",' ').replace("-",' ')) 39 k=0 40 for i in data['data']['rankings']: 41 print(("{:-^5} {:一^12} {:-^10} {:-^2}".format(i['ranking'],i['univNameCn'],i['score'],i['univCategory'] )).replace("一",' ').replace("-",' ')) 42 k+=1 43 if k==30: 44 break 45 #保存数据,生成xlsx 46 def saveHTMLText(lst): 47 try: 48 #对df数据类型中的columns赋值 49 headers = ['排名','学校名称','总分','类型'] 50 #对df数据类型中的index赋值 51 index = [i for i in range(1,len(lst)+1)] 52 #将数据列表转换为DataFrame对象 53 df = pd.DataFrame(lst,columns=headers,index=index) 54 #判断磁盘里是否存在目标文件夹 55 if not os.path.exists('D:/asd.xlsx'): 56 #不存在,则创建该文件夹 57 os.makedirs('D:/asd.xlsx') 58 #生成xlsx文件 59 df.to_excel('D:/asd.xlsx') 60 else: 61 df.to_excel('D:/asd.xlsx') 62 print("保存成功") #返回成功提示 63 except: 64 print("保存失败") #返回失败提示 65 #2 66 #读取文件并浏览 67 import pandas as pd 68 df=pd.read_excel('D:/asd.xlsx') 69 df.head() 70 #删除无效列 71 df.drop('省市',axis=1,inplace=True) 72 df.head() 73 #重复值处理 74 df.duplicated() 75 #空值处理 76 df['学校名称'].isnull() 77 #异常值处理 78 df.describe() 79 #3 80 #使用jieba分词 81 file=open('D:/asd.csv',encoding='utf_8') 82 user_dict=file.read() 83 print(user_dict) 84 #读取文件,用jieba进行文本分词并保存文件中 85 asd_excel=open('D:/asd.csv','r',encoding='utf_8').read() 86 #读取文件,编码格式utf-8,防止处理中文出错 87 import jieba.posseg as psg 88 asd_words_with_attr=[(x.word,x.flag)for x in psg.cut(asd_excel) if len(x.word)>=2] 89 #x.word为词本身,x.flag为词性 90 print(len(asd_words_with_attr))#输出 91 with open('cut_words.txt','w+')as f: 92 for x in asd_words_with_attr: 93 f.write('{0}\t{1}\n'.format(x[0],x[1])) 94 #从cut_words.txt中读取带词性的分词结果列表 95 asd_words_with_attr=[] 96 with open('cut_words.txt','w+')as f: 97 for x in f.readlines(): 98 pair=x.split() 99 asd_words_with_attr.append((pair[0],pair[1])) 100 #stop_attr中存放要过滤掉的词性列表 101 stop_attr=['vn','n'] 102 #过滤清洗数据,将结果存放在words中 103 words=[x[0]for x in asd_words_with_attr if x[1] not in stop_attr] 104 #4 105 import pandas as pd 106 #数据加载与数据集统计信息查看 107 #使用pd.read_excel()的方法,将本地的asd.xlsx数据加载到DataFrame中 108 asd_df=pd.read_excel(r'D:/asd.xlsx') 109 asd_df.head() 110 #使用describe()方法查看数据表中的个数值字段的统计信息,了解数据集大致特征 111 asd_df.describe() 112 import pandas as pd 113 import numpy as np 114 import matplotlib.pyplot as plt 115 import seaborn as sns 116 asd=pd.read_excel(r'D:/asd.xlsx') 117 #数据可视化 118 #直方图 119 sns.distplot(asd['总得分'],label='asd') 120 plt.grid() 121 plt.legend(loc='upper right') 122 #散点图 123 # 用来正常显示中文标签 124 plt.rcParams['font.sans-serif'] = ['SimHei'] 125 # 用来正常显示负号 126 plt.rcParams['axes.unicode_minus'] = False 127 #x,y轴 128 plt.xlabel("2021排名") 129 plt.ylabel("总得分") 130 sns.regplot(x='2021排名',y='总得分',data=asd,color='r') 131 # kind='hex' 132 sns.jointplot(x="排名",y="热度",data=rank,kind='hex') 133 # kind='kde' 134 sns.jointplot(x="排名",y="热度",data=rank,kind="kde",space=0,color='g') 135 #盒图 136 plt.xlabel("2021排名") 137 plt.ylabel("总得分") 138 sns.boxplot(x='2021排名',y='总得分',data=asd) 139 #绘制饼图 140 #前30所学校 141 plt.figure(figsize=(12,8),dpi=80) 142 # 各部分标签 143 label_list = ["综合", "理工", "师范"] 144 # 各部分大小 145 size = [18, 10, 2] 146 color = ["red", "green", "blue"] 147 # 各部分突出值 148 explode = [0.05, 0, 0] 149 patches, l_text, p_text = plt.pie(size, explode=explode, colors=color,\ 150 labels=label_list, labeldistance=1.1,\ 151 autopct="%1.1f%%", shadow=False, startangle=90, pctdistance=0.6) 152 # 设置横轴和纵轴大小相等,这样饼才是圆的 153 plt.axis("equal") 154 plt.legend() 155 plt.show() 156 #柱状图 157 sns.countplot(asd['学校类型']) 158 sns.countplot(asd['省市']) 159 #回归图 160 fig,axes=plt.subplots(2,2) 161 #1.默认绘图效果 162 sns.regplot(x='2021排名',y='总得分',data=asd,ax=axes[0][0]) 163 #2.ci参数可以控制是否显示置信区间 164 sns.regplot(x='2021排名',y='总得分',data=asd,ci=None,ax=axes[0][1]) 165 #3.mark参数可以设置数据点格式,color参数可以设置颜色 166 sns.regplot(x='2021排名',y='总得分',data=asd,color='g',marker='*',ax=axes[1][0]) 167 #4.fit_reg参数可以控制是否显示拟合的直线 168 sns.regplot(x='2021排名',y='总得分',data=asd,color='g',marker='+',fit_reg=False,ax=axes[1][1]) 169 #选择排名以及总得分两个特征变量,绘制分布图 170 import pandas as pd 171 import matplotlib.pyplot as plt 172 colnames=["排名","总得分"] 173 df=pd.read_excel('D:/asd.xlsx',skiprows=1,names=colnames) 174 X = df.排名 175 Y = df.总得分 176 def A(): 177 plt.scatter(X,Y,color="blue",linewidth=2) 178 plt.title("总情况",color="blue") 179 plt.grid() 180 plt.show() 181 def B(): 182 plt.scatter(X,Y,color="green",linewidth=2) 183 plt.title("总情况",color="blue") 184 plt.grid() 185 plt.show() 186 def func(p,x): 187 a,b,c=p 188 return a*x*x+b*x+c 189 def error(p,x,y): 190 return func(p,x)-y 191 def main(): 192 plt.figure(figsize=(10,6)) 193 p0=[0,0,0] 194 Para = leastsq(error,p0,args=(X,Y)) 195 a,b,c=Para[0] 196 print("a=",a,"b=",b,"c=",c) 197 plt.scatter(X,Y,color="blue",linewidth=2) 198 x=np.linspace(0,20,20) 199 y=a*x*x+b*x+c 200 plt.plot(x,y,color="blue",linewidth=2,) 201 plt.title("总情况") 202 plt.grid() 203 plt.show() 204 print(A()) 205 print(B()) 206 print(main()) 207 #5 208 #计算回归系数与截距 209 import numpy as np 210 import pandas as pd 211 import matplotlib.pyplot as plt 212 import seaborn as sns 213 plt.figure 214 from sklearn.linear_model import LinearRegression 215 asd=pd.read_excel('D:/asd.xlsx') 216 pt=LinearRegression() 217 pt.fit(asd[['2021排名']],asd['总得分']) 218 print("回归系数为:",pt.coef_) 219 print("截距:",pt.intercept_) 220 #建立变量之间的回归方程 221 a=float(pt.coef_) 222 b=float(pt.intercept_) 223 print('y={:.2f}x+{:.2f}'.format(a,b)) 224 #当x=20时 225 y=a*20+b 226 print(y) 227 print('当x=20,总得分:',y) 228 #画出散点图 229 import seaborn as sns 230 sns.regplot(x='2021排名',y='总得分',data=asd).format(a,b)) 231 #调用LinearRegression的predict方法可以进行预测 232 predict_model.predict(X)[0:10] 233 #模型评估--训练集和测试集的划分 234 import sklearn 235 from sklearn import datasets 236 from sklearn import model_selection 237 X_train,X_text,Y_rain,Y_text=sklearn.model_selection.train_text_split(X,asd_df.drop("总得分"),text_size=0.33,random_state=5) 238 #构建模型 239 predict_model_2=LinearRegression() 240 predict_model_2.fit(X_train,Y_train) 241 predict_train=predict_model_2.predict(X_train) 242 predict_text=predict_model_2.predict(X_text) 243 #打印误差 244 print("模型训练集的误差:",np.mean((Y_train-lm.predict(X_train))**2)) 245 print("模型测试集的误差:",np.mean((Y_text-lm.predict(X_text))**2)) 246 #6 247 #数据持久化 248 import pymysql 249 if __name__ == "__main__": 250 args = dict( 251 host = 'localhost', 252 user = 'root', 253 passwd = '...your password', 254 db = 'pydb', 255 charset = 'utf8' 256 ) 257 conn = pymysql.connect(**args) 258 cursor = conn.cursor() 259 cursor.execute("drop table if exists test") 260 cursor.execute("create table test(id int,name varchar(20))") 261 cursor.execute('insert into test values(1," ")') 262 cursor.execute('insert into test values(2," ")') 263 cursor.execute('insert into test values(3," ")') 264 conn.commit() 265 conn.close()
五、总结(10 分)
1.经过对主题数据的分析与可视化,可以得到哪些结论?是否达到预期的目标?
通过数据分析以及可视化发现,排行前几的大学总得分相差较大,但排名相对靠后总得分分数值相差较小,数据逐渐接近一条斜线。从前30名的排行饼图来看,综合大学所占的比例较高,而从整体来看,理工大学所占的比例较高,较发展地区的学校也更加多。从回归方程和拟合曲线可以看出排名靠前和靠后的散点基本都分布在曲线的上方,而排名靠中间的大多是分布在曲线的下方。
2.在完成此设计过程中,得到哪些收获?以及要改进的建议?
通过这次的设计,更好的巩固了我们所学习的知识,除此之外还学到了许多课外的知识。懂得利用网络来解惑,更加依赖自己。虽然在这过程中也遇到了不少难题,但是这让我们更加有动力去学习新的东西,学无止境。