爬取知乎热搜榜进行数据分析和数据可视化


一、选题背景

随着科技经济的发展,社会中发生的重大事件我们都可以从各大软件中得知,知乎热榜是我们了解时事的一个重要途径,但是如果我们没有那么时间来刷知乎,但是还是想要了解一天中发生的热门事件,我们该怎么办呢?在这里,我想到了通过知乎爬虫的手段,获取知乎热榜的标题和简介,保存到本地文件,,从而获取到每一天的知乎热榜内容,这样,我们只需要查看本地文件内容,就可以快速的了解今天一天的时事。要达到的数据分析目标是 通过爬虫知乎热搜榜爬获的数据画出排行与热度的图形,从而分析排行与热度的线性关系。

二、主题式网络爬虫设计方案

1、主题式网络爬虫名称:爬取知乎热搜榜

2.爬取内容:爬取网页热搜排名,标题,热度值。

数据特征:内容是随机改变的主要以文字和数字为主。

3.方案概述:首先访问网页得到状态码200,分析网页源代码,找出所需要的的标签,逐个提取标签保存到相同路径csv文件中,读取改文件,进行数据清洗,数据模型分析,数据可视化处理,绘制分布图,用最小二乘法分析两个变量间的二次拟合方程和绘制拟合曲线。

技术难点:获取知乎热搜榜的url网址,获取相对应的标签;数据输出时列名无法对齐;文本分析用到的jieba分词需要上网搜索;画折线图出现无法将数据转换为浮点型的错误;利用最小二乘法拟合曲线;

三、主题页面的结构特征分析

1、搜爬取的知乎网站的url为

 url = 'https://tophub.today/n/mproPpoq6O'

通过游览器搜索右击点击‘’检查‘’查看‘’网络‘’与“元素”,获取网页

主题页面的结构和特征分析:所要爬取的热度数据在标签‘td’里面,标题在标签‘<a href> .... <a>’里面。

四、网络爬虫程序设计

1.数据爬取与采集

 1 url = 'https://tophub.today/n/mproPpoq6O'
 2 header = {'user-agent':'Mozilla/5.0'}
 3 r = requests.get(url, headers=header)
 4 r.raise_for_status()
 5 r.encoding = r.apparent_encoding
 6 r.text
 7 html = r.text
 8 title = re.findall('<a href=.*? target="_blank" .*?>(.*?)</a>',html)[3:23]
 9 redu = re.findall('<td>(.*?)</td>',html)[0:120]
10 #print(title)
11 #print(redu)
12 print('{:^95}'.format('知乎热度榜单'))
13 print('{:^5}\t{:^40}\t{:^10}'.format('排名','标题','热度(单位:万)'))
14 num = 20
15 lst = []
16 for i in range(num):
17     print('{:^5}\t{:^40}\t{:^10}'.format(i+1, title[i], redu[i][:-3]))
18     lst.append([i+1, title[i], redu[i][:-3]])
19 df = pd.DataFrame(lst, columns=['排名','标题','热度(单位:万)'])
20 pd.set_option('display.unicode.ambiguous_as_wide',True) #使输出的列与列值对齐
21 pd.set_option('display.unicode.east_asian_width',True) #使输出的列与列值对齐
22 #df.to_excel('rank.xlsx')
23 rank = r'rank.xlsx'
24 df

2.对数据进行清洗和处理

1 #读取csv文件
2 rank=pd.DataFrame(pd.read_csv('知乎热搜榜.xlsx'))
3 print(rank.head())

1 #删除无效列
2 df.drop('热度(单位:万)',axis=1,inplace=True)

   

1 #检查是否有空值
2 df['热度(单位:万)'].isnull.value_counts() 

  

1 #检查是否有重复值
2 df.duplicated()

   

1 #异常值处理
2 df.describe()

      

 3.文本分析:jieba分词

1 def fit(text):
2      return jieba.cut(text)
3 df['标题']=df['标题'].apply(fit)
4 df.sample(5)

     4.数据分析与可视化

1 #数据分析
2 from sklearn.linear_model import LinearRegression
3 X = df.drop("标题",axis=1)
4 predict_model = LinearRegression()
5 predict_model.fit(X,df['热度(单位:万)'])
6 print("回归系数为:",predict_model.coef_)
 1 #绘制排名与热度的折线图与柱状图
 2 def python():
 3     plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
 4     x = df['排名']
 5     y = df['热度(单位:万)']
 6     plt.xlabel('排名')
 7     plt.ylabel('热度(单位:万)')
 8     plt.plot(x,y)
 9     plt.scatter(x,y)
10     plt.title('排名与热度的折线图')
11     plt.show()
12 python()
13 plt.rcParams['font.sans-serif'] = ['SimHei']
14 plt.bar(range(1,9),redu[:8])
15 plt.xlabel('排名')
16 plt.ylabel('热度(单位:万)')
17 plt.title('排名与热度的柱状图')
18 plt.show()

 

 

5.根据数据之间的关系,分析两个变量之间的相关系数,画出散点图,并建立变 量之间的回归方程

 

 1 #画出散点图,求出排名与截距,构造出一元方程
 2 df['热度(单位:万)']=list(map(int,df['热度(单位:万)'])) #将热度的数据类型转换成与排名相同的int类型
 3 plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
 4 plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号
 5 N=20
 6 x=np.random.rand(N)
 7 y=np.random.rand(N)
 8 size=50
 9 plt.xlabel("排名")
10 plt.ylabel("热度")
11 plt.scatter(x,y,size,color='b',alpha=0.5,marker="o")
12 #散点图  kind='reg'
13 sns.jointplot(x="排名",y="热度(单位:万)",data=df,kind='reg')
14 #  kind='hex'
15 sns.jointplot(x="排名",y="热度(单位:万)",data=df,kind='hex')
16 # kind='kde'
17 sns.jointplot(x="排名",y="热度(单位:万)",data=df,kind="kde",space=0,color='g')
18 #求出排名、截距
19 x=np.array(df['排名']).reshape(-1,1)
20 y=np.array(df['热度(单位:万)']).reshape(-1,1)
21 predict_model.fit(x,y)
22 x=np.array(df['排名']).reshape(-1,1)
23 y=np.array(df['热度(单位:万)']).reshape(-1,1)
24 predict_model.fit(x,y)
25 print("回归系数为:",predict_model.coef_)
26 print("截距:",predict_model.intercept_)
27 #有图可知所构造的一元方程的斜率为-49.72,截距为913.56
28 #所以方程为y=-49.72+913.56

 

 

 

 

 

 

 

 

 

 1 import matplotlib.pyplot as plt
 2 plt.rcParams['font.sans-serif']='SimHei'
 3 #设置中文显示
 4 plt.figure(figsize=(6,6))
 5 #将画布设定为正方形,则绘制的饼图是正圆
 6 label=['热度0-400万第一区限','热度400-800万第二区限','热度800-1200万第三区限','热度1200-1600万第四区限']
 7 #定义饼图的标签,标签是列表
 8 explode=[0.01,0.01,0.01,0.01]
 9 #设定各项距离圆心n个半径
10 values=[13,4,2,1]
11 # 13,4,2,1 分别为热度在0-400万,400-800,800-1200,1200-1600 区间的个数
12 plt.pie(values,explode=explode,labels=label,autopct='%1.1f%%')
13 #绘制饼图
14 plt.title('热度数值分布值饼图')
15 #绘制标题
16 plt.savefig('./热度数值分布值饼图')
17 #保存图片
18 plt.show()

 

 

 1 #选择排名和热度两个特征变量,绘制分布图,用最小二乘法分析两个变量间的二次拟合方程和拟合曲线
 2 import scipy.optimize as opt
 3 colnames=[" ","排名","标题","热度(单位:万)"]
 4 df = pd.read_excel('rank.xlsx',skiprows=1,names=colnames)
 5 X = df['排名']
 6 Y = df['热度(单位:万)']
 7 Z = df['标题']
 8 def A():
 9     plt.scatter(X,Y,color="blue",linewidth=2)
10     plt.title("排名",color="g")
11     plt.grid()
12     plt.show()
13 def B():
14     plt.scatter(X,Y,color="b",linewidth=2)
15     plt.title("热度",color="g")
16     plt.grid()
17     plt.show()
18 def fit1(p,x):
19     p=a,b,c #a、b、c 分别为二次函数的系数、截距
20     return a*x*x+b*x+c  #用于拟合的二次函数
21 def fit2(p,x,y):
22     return func(p,x)-y  
23 def main():
24     plt.figure(figsize=(10,6))
25     p0=[0,0,0]    #第一次猜测的函数拟合参数
26     Para = opt.leastsq(error,p0,args=(X,Y))  #进行最小二乘拟合
27     a,b,c=Para[0]
28     print("a=",a,"b=",b,"c=",c)
29     plt.scatter(X,Y,color="r",linewidth=2)
30     x=np.linspace(0,20,20)
31     y=a*x*x+b*x+c
32     plt.plot(x,y,color="r",linewidth=2,)
33     plt.title("热度值分布")
34     plt.grid()
35     plt.show()
36 print(A())
37 print(B())
38 print(main())
39 #有图可知所拟合的二次函数的系数和截距分别为3.53,-127.49,1235.256
40 #所以此二次函数为y=3.53*x*x-127.49*x+1235.256

 

 

 

 

 6.数据持久化

1 #我采用的是使用df.to_csv 将df中的数据保存到csv中,具体路径为C:\Users\86181\rank.xlsx 以此已实现数据持久化
2 filename='rank.csv'
3 headers=['排名','标题','热度']
4 index=[i for i in range(1,len(html)+1)]
5 df2=pd.DataFrame(html,columns=headers,index=index)
6 pd.DataFrame.to_csv(df2,filename)

7.将以上各部分的代码汇总,附上完整程序代码

 

  1 import requests
  2 import jieba
  3 import xlrd
  4 import pandas as pd
  5 import openpyxl
  6 import matplotlib
  7 import matplotlib.pyplot as plt
  8 import numpy as np
  9 import seaborn as sns
 10 from sklearn.linear_model import LinearRegression
 11 #睡眠模拟用户
 12 time.sleep(1+random.random())
 13 #获取html网页
 14 url = 'https://tophub.today/n/mproPpoq6O'
 15 header = {'user-agent':'Mozilla/5.0'}
 16 #请求超时时间为30秒
 17 r = requests.get(url,timeout = 30,headers=header)
 18 #如果状态码不是200,则引发日常
 19 r.raise_for_status()
 20 #配置编码
 21 r.encoding = r.apparent_encoding
 22 #获取源代码
 23 r.text
 24 html = r.text
 25 title = re.findall('<a href=.*? target="_blank" .*?>(.*?)</a>',html)[3:23]
 26 redu = re.findall('<td>(.*?)</td>',html)[0:120]
 27 #print(title)
 28 #print(redu)
 29 print('{:^95}'.format('知乎热度榜单'))
 30 print('{:^5}\t{:^40}\t{:^10}'.format('排名','标题','热度(单位:万)'))
 31 num = 20
 32 lst = []
 33 for i in range(num):
 34     print('{:^5}\t{:^40}\t{:^10}'.format(i+1, title[i], redu[i][:-3]))
 35     lst.append([i+1, title[i], redu[i][:-3]])
 36 df = pd.DataFrame(lst, columns=['排名','标题','热度(单位:万)'])
 37 pd.set_option('display.unicode.ambiguous_as_wide',True) 
 38 #使输出的列与列值对齐
 39 pd.set_option('display.unicode.east_asian_width',True) 
 40 #使输出的列与列值对齐
 41 #df.to_excel('rank.xlsx')
 42 rank = r'rank.xlsx'
 43 df
 44 #读取csv文件
 45 rank=pd.DataFrame(pd.read_csv('知乎热搜榜.xlsx'))
 46 print(rank.head())
 47 #删除无效列
 48 df.drop('标题',axis=1,inplace=True)
 49 #检查是否有空值
 50 df['热度(单位:万)'].isnull.value_counts()
 51 #检查是否有重复值
 52 df.duplicated()
 53 #异常值处理
 54 df.describe()
 55 #利用jieba分词进行文本分析
 56 def fit(text):
 57      return jieba.cut(text)
 58 df['标题']=df['标题'].apply(fit)
 59 df.sample(5)
 60 #数据分析
 61 from sklearn.linear_model import LinearRegression
 62 X = df.drop("标题",axis=1)
 63 predict_model = LinearRegression()
 64 predict_model.fit(X,df['热度(单位:万)'])
 65 print("回归系数为:",predict_model.coef_)
 66 #绘制排名与热度的折线图与柱状图
 67 import requests
 68 import jieba
 69 import xlrd
 70 import pandas as pd
 71 import openpyxl
 72 import matplotlib
 73 import matplotlib.pyplot as plt
 74 import numpy as np
 75 import seaborn as sns
 76 from sklearn.linear_model import LinearRegression
 77 #睡眠模拟用户
 78 time.sleep(1+random.random())
 79 #获取html网页
 80 url = 'https://tophub.today/n/mproPpoq6O'
 81 header = {'user-agent':'Mozilla/5.0'}
 82 #请求超时时间为30秒
 83 r = requests.get(url,timeout = 30,headers=header)
 84 #如果状态码不是200,则引发日常
 85 r.raise_for_status()
 86 #配置编码
 87 r.encoding = r.apparent_encoding
 88 #获取源代码
 89 r.text
 90 html = r.text
 91 title = re.findall('<a href=.*? target="_blank" .*?>(.*?)</a>',html)[3:23]
 92 redu = re.findall('<td>(.*?)</td>',html)[0:120]
 93 #print(title)
 94 #print(redu)
 95 print('{:^95}'.format('知乎热度榜单'))
 96 print('{:^5}\t{:^40}\t{:^10}'.format('排名','标题','热度(单位:万)'))
 97 num = 20
 98 lst = []
 99 for i in range(num):
100     print('{:^5}\t{:^40}\t{:^10}'.format(i+1, title[i], redu[i][:-3]))
101     lst.append([i+1, title[i], redu[i][:-3]])
102 df = pd.DataFrame(lst, columns=['排名','标题','热度(单位:万)'])
103 def python():
104     plt.rcParams['font.sans-serif'] = ['SimHei'] 
105     # 用来正常显示中文标签
106     x = df['排名']
107     y = df['热度(单位:万)']
108     plt.xlabel('排名')
109     plt.ylabel('热度(单位:万)')
110     plt.plot(x,y)
111     plt.scatter(x,y)
112     plt.title('排名与热度的折线图')
113     plt.show()
114 python()
115 plt.rcParams['font.sans-serif'] = ['SimHei']
116 plt.bar(range(1,9),redu[:8])
117 plt.xlabel('排名')
118 plt.ylabel('热度(单位:万)')
119 plt.title('排名与热度的柱状图')
120 plt.show()
121 #画出散点图,求出排名与截距,构造出一元方程
122 df['热度(单位:万)']=list(map(int,df['热度(单位:万)'])) 
123 #将热度的数据类型转换成与排名相同的int类型
124 plt.rcParams['font.sans-serif'] = ['SimHei']  
125 # 用来正常显示中文标签
126 plt.rcParams['axes.unicode_minus'] = False 
127 # 用来正常显示负号
128 N=20
129 x=np.random.rand(N)
130 y=np.random.rand(N)
131 size=50
132 plt.xlabel("排名")
133 plt.ylabel("热度")
134 plt.scatter(x,y,size,color='b',alpha=0.5,marker="o")
135 #散点图  kind='reg'
136 sns.jointplot(x="排名",y="热度(单位:万)",data=df,kind='reg')
137 #  kind='hex'
138 sns.jointplot(x="排名",y="热度(单位:万)",data=df,kind='hex')
139 # kind='kde'
140 sns.jointplot(x="排名",y="热度(单位:万)",data=df,kind="kde",space=0,color='g')
141 #求出排名、截距
142 x=np.array(df['排名']).reshape(-1,1)
143 y=np.array(df['热度(单位:万)']).reshape(-1,1)
144 predict_model.fit(x,y)
145 x=np.array(df['排名']).reshape(-1,1)
146 y=np.array(df['热度(单位:万)']).reshape(-1,1)
147 predict_model.fit(x,y)
148 print("回归系数为:",predict_model.coef_)
149 print("截距:",predict_model.intercept_)
150 #有图可知所构造的一元方程的斜率为-49.72,截距为913.56
151 #所以方程为y=-49.72+913.56
152 import matplotlib.pyplot as plt
153 plt.rcParams['font.sans-serif']='SimHei'
154 #设置中文显示
155 plt.figure(figsize=(6,6))
156 #将画布设定为正方形,则绘制的饼图是正圆
157 label=['热度0-400万第一区限','热度400-800万第二区限','热度800-1200万第三区限','热度1200-1600万第四区限']
158 #定义饼图的标签,标签是列表
159 explode=[0.01,0.01,0.01,0.01]
160 #设定各项距离圆心n个半径
161 values=[13,4,2,1]
162 # 13,4,2,1 分别为热度在0-400万,400-800,800-1200,1200-1600 区间的个数
163 plt.pie(values,explode=explode,labels=label,autopct='%1.1f%%')
164 #绘制饼图
165 plt.title('热度数值分布值饼图')
166 #绘制标题
167 plt.savefig('./热度数值分布值饼图')
168 #保存图片
169 plt.show()
170 #选择排名和热度两个特征变量,绘制分布图,用最小二乘法分析两个变量间的二次拟合方程和拟合曲线
171 import scipy.optimize as opt
172 colnames=[" ","排名","标题","热度(单位:万)"]
173 df = pd.read_excel('rank.xlsx',skiprows=1,names=colnames)
174 X = df['排名']
175 Y = df['热度(单位:万)']
176 Z = df['标题']
177 def A():
178     plt.scatter(X,Y,color="blue",linewidth=2)
179     plt.title("排名",color="g")
180     plt.grid()
181     plt.show()
182 def B():
183     plt.scatter(X,Y,color="b",linewidth=2)
184     plt.title("热度",color="g")
185     plt.grid()
186     plt.show()
187 def fit1(p,x):
188     p=a,b,c 
189     #a、b、c 分别为二次函数的系数、截距
190     return a*x*x+b*x+c  
191    #用于拟合的二次函数
192 def fit2(p,x,y):
193     return func(p,x)-y  
194 def main():
195     plt.figure(figsize=(10,6))
196     p0=[0,0,0]   
197     #第一次猜测的函数拟合参数
198     Para = opt.leastsq(error,p0,args=(X,Y))  
199     #进行最小二乘拟合
200     a,b,c=Para[0]
201     print("a=",a,"b=",b,"c=",c)
202     plt.scatter(X,Y,color="r",linewidth=2)
203     x=np.linspace(0,20,20)
204     y=a*x*x+b*x+c
205     plt.plot(x,y,color="r",linewidth=2,)
206     plt.title("热度值分布")
207     plt.grid()
208     plt.show()
209 print(A())
210 print(B())
211 print(main())
212 #有图可知所拟合的二次函数的系数和截距分别为3.53,-127.49,1235.256
213 #所以此二次函数为y=3.53*x*x-127.49*x+1235.256
214 #我采用的是使用df.to_csv 将df中的数据保存到csv中,具体路径为C:\Users\86181\rank.xlsx 以此已实现数据持久化
215 filename='rank.csv'
216 headers=['排名','标题','热度']
217 index=[i for i in range(1,len(html)+1)]
218 df2=pd.DataFrame(html,columns=headers,index=index)
219 pd.DataFrame.to_csv(df2,filename)

 

五、总结

1.经过对数据的分析和可视化,从回归方程和拟合曲线可以看出散点大部分都落在曲线上,说明热度是随着排名的递增而递增的。又从饼状图图可以看出热度大部分停留在0-400W之间,通过爬虫可分析排名与热度之间的关系,从而画出各种图形。

2.小结:在这次对知乎热搜榜的分析的过程中,我从中学会了不少函数及用法。遇见了很多问题,例如通过爬虫爬取的数据因为列名是中文导致的列名不能对齐、在画图是因为x轴、y轴数据类型不相同而出错要先将两者的数据类型设置为同一类型、文本分析用到的jieba分词的用法需要通过上网搜索,通过观看视频,百度搜索,CSDN搜索等方法去找寻答案。这三个星期来也养成了独立思考的习惯,极大提升了我对python爬虫的兴趣,通过爬虫可以学习到很多知识。

 

 

    

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM