爬取中国大学排行

一、选题的背景(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.在完成此设计过程中,得到哪些收获?以及要改进的建议?

       通过这次的设计,更好的巩固了我们所学习的知识,除此之外还学到了许多课外的知识。懂得利用网络来解惑,更加依赖自己。虽然在这过程中也遇到了不少难题,但是这让我们更加有动力去学习新的东西,学无止境。

posted @ 2021-12-25 14:44  Lin17  阅读(429)  评论(0编辑  收藏  举报