首发于AIGC开发者
资讯类app用户热度及资讯类型分析

资讯类app用户热度及资讯类型分析

一、背景分析

随着今日头条的崛起,资讯类app已经成为各巨头争夺流量入口的又一主要阵地。除了百度和阿里文娱旗下UC浏览器分别推出了百度百家和UC头条,腾讯也在发布天天快报后投资了趣头条,而趣头条也在18年9月赴美上市。这些资讯类app有一个共同的特点,他们都是基于机器学习和数据挖掘的资讯推荐类产品,为用户推荐其感兴趣的信息

通过百度指数搜索“今日头条”、“百度百家”、“趣头条”以及“UC头条”四个关键字,我们可以发现今日头条搜索指数和搜索趋势均远高于其他几款产品,说明其产品热度一直较高,受到了更多人的关注。

近一个月四款app搜索热度
2011年以来四款app索索热度趋势

趣头条近期的发展速度也很迅猛,尤其是9月15日前后,其热度超过了今日头条,可能是因为上市新闻引起了大家的关注。相对而言,百度百家和UC头条则一直不温不火,热度一直不高。

二、问题定位

结合以上背景,本文主要对以下几个问题进行探索。

不同app之间横向对比

  1. 比较不同app的用户热度,即活跃度(从阅读量、评论量、资讯量及作者量等角度分析)
  2. 不同app的资讯类型特点(比较阅读量最高、资讯量最多的资讯类型都是哪些,不同app中主要资讯内容是否有差异)

以今日头条为例,不同时段用户热度的纵向对比

  1. 不同周期的用户热度趋势特点(一周中,不同周期用户浏览行为和作者产出特点的差异)
  2. 不同时刻的用户热度趋势特点(一天中,不同时刻用户浏览行为和作者产出特点的差异)

目的:分析不同app用户浏览以及内容创作者产出特点,为用户浏览资讯以及创作者发布资讯提供建议。

三、获取数据

本文数据抓#取于“自媒咖”,抓#取方式是利用某数据抓#取工具在每晚十二点后开始抓#取前两天的数据。最终抓#取了四款app的文章相关信息,其资讯发布时间范围在2018-10-14至2018-10-21日之间。

自媒咖

本文数据分析所使用到的工具:

  1. Mysql(数据前期的预处理:如数据整合、去重等操作)
  2. Python (数据进一步清洗及分析:如描述性统计以及可视化等)

因为考虑到如果数据抓#取时间距离资讯发布时间太近时,资讯的阅读量等信息会和较早发布的资讯产生较明显差异。因此为尽量避免这种因素的影响,我们选取数据抓#取时间与资讯发布时间相隔一天以上的数据进行分析,例如在10月22日凌晨抓#取前两日(20日和21日)的数据,取20日的数据进行分析。

四、数据描述

数据量:本文通过Mysql进行数据预处理,最终筛选出122119条数据,并利用Python进行数据处理和分析。

字段释义:

五、数据清洗

因篇幅限制,(5.1部分)Mysql中数据处理过程请详见另一篇,本文主要介绍Python进行数据分析部分

5.1 利用Mysql进行数据前期处理

5.1.1 先将抓#取的数据导入到Mysql

5.1.2 数据预处理

如将时间数据转为时间类型、提取资讯类型等,并创建表存储数据

5.1.3 利用时间间隔选取数据,并整合数据

  1. 选取数据抓#取时间与资讯发布时间相隔一天以上的数据(因每日采集数据量有限,而趣头条数据量较大,故只能抓#取采集时间前一天的部分数据。所以我们仅利用其数据分析趣头条资讯的类型特点,但在进行app数据横向对比时将剔除这部分数据);
  2. 重复数据处理;
  3. 导出为csv文件。


5.2 利用Python进行数据清洗

5.2.1导入相关包及数据

import warnings
warnings.filterwarnings('ignore')
import pandas as pd

filedf=pd.read_csv('Python数据/appdata.csv')

5.2.2查看数据

#查看前三项
filedf.head(3)
#查看数据量及数据格式
filedf.info()
>>>
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 122119 entries, 0 to 122118
Data columns (total 8 columns):
pub_date       122119 non-null object
ex_date        122119 non-null object
title          122119 non-null object
author         122119 non-null object
num_read       122119 non-null int64
num_comment    122119 non-null int64
resource       122119 non-null object
type           122119 non-null object
dtypes: int64(2), object(6)
memory usage: 7.5+ MB
  • pub_date及ex_date只需要保留到天;
  • 无缺失数据,说明在前期数据处理中缺失值已经被剔除。
#查看数据的描述性统计
filedf.describe()
>>>
        num_read	num_comment
count	1.221190e+05	122119.000000
mean	5.683611e+03	9.555647
std	4.734009e+04	263.414244
min	0.000000e+00	0.000000
25%	0.000000e+00	0.000000
50%	4.300000e+01	0.000000
75%	8.510000e+02	0.000000
max	4.261331e+06	39120.000000
  • 各资讯间阅读量及评论量差异较大,大多数(约75%以上)的资讯评论量为零,阅读量不足1000;
  • 阅读量和评论量均无负值,但评论量最多的资讯有近4万条评论,需注意异常值。
#查看不同app平台数据的描述性统计
filedf.groupby('resource').describe()

对各类resource的num_read和num_comment数据进行整理,剔除三个标准差外的数据,并观察其数据分布。

import numpy as np
filedf_cleanread=filedf[np.abs(filedf['num_read']-filedf['num_read'].mean())<=(3*filedf['num_read'].std())]
filedf_cleancomm=filedf[np.abs(filedf['num_comment']-filedf['num_comment'].mean())<=(3*filedf['num_comment'].std())]

sns.boxplot(x='num_read',y='resource',hue='resource',data=filedf_cleanread)
plt.title('Distribution of the num_read in different resources')
plt.show()
sns.boxplot(x='num_comment',y='resource',hue='resource',data=filedf_cleancomm)
plt.title('Distribution of the num_comment in different resources')
plt.show()
  1. 今日头条上资讯的阅读量和评论量均较高,UC头条次之,百度百家的评论数量数据可能采集有缺失,导致为空值。
  2. 资讯阅读量及评论量的数据波动较明显,说明每个平台都会存在一定的“爆文”数量,但大多数资讯的阅读和评论量不高。

5.2.3数据清洗

1.提取pub_date中的日期和时刻数据

#截取资讯发布日期
filedf['pub_day']=filedf['pub_date'].map(lambda x:x.split(' ')[0])
#截取资讯发布时段
filedf['pub_hour']=filedf['pub_date'].map(lambda x:x.split(' ')[1][0:2])
#可以先删掉pub_date和ex_date两个字段
filedf.drop(['pub_date','ex_date'],axis=1,inplace=True)
filedf.head()

2.去除重复值

print('去重之前的数据量:',filedf.shape)
filedf=filedf.drop_duplicates()
print('去重之前的数据量:',filedf.shape)
>>>
去重之前的数据量: (122119, 8)
去重之前的数据量: (106355, 8)

3.resource中数据进行替换为资讯来源平台

#先查看有哪些项
filedf['resource'].value_counts()
>>>
qu         40823
uc         30930
toutiao    26833
baidu       7769
Name: resource, dtype: int64

#构造函数,进行替换
def renamefc(x):
    if x=='toutiao':
        y='今日头条'
    elif x=='qu':
        y='趣头条'
    elif x=='uc':
        y='UC头条'
    else:
        y='百度百家'
    return y

filedf['resource']=filedf['resource'].map(lambda a:renamefc(a))
filedf.head()

4.选取“今日头条”,“百度百家”,“UC头条”数据进行分析

#趣头条数据缺失较多,只采集了部分时间段的数据
filedf['pub_hour'][filedf['resource']=='趣头条'].value_counts()
>>>
22    13454
23     7640
21     7531
15     3714
16     3570
20     3498
19      840
00      576
Name: pub_hour, dtype: int64

#不同app采集到的数据量
filedf['resource'].value_counts()
>>>
趣头条     40823
UC头条    30930
今日头条    26833
百度百家     7769
Name: resource, dtype: int64

#不同app资讯量
fig=plt.figure(figsize=(14,6))

plt.subplot(1,2,1)
plt.pie(filedf['resource'].value_counts(),labels=filedf['resource'].value_counts().index,
       autopct='%1.2f%%',textprops={'fontsize':'12'},explode=(0,0.1,0,0))
plt.title('The propotion of different resourses',fontsize=15)

plt.subplot(1,2,2)
filedf['resource'].value_counts().plot(kind='bar',width=0.6)
plt.xticks(fontsize=12,rotation=45)
plt.title('The data size of different resources',fontsize=15)

虽然只采集了趣头条部分时间段的数据,但是其发文量已经远超其他类别app的数据量。在本文的研究中,仅利用趣头条的数据来分析其资讯类型特点,不进行用户热度的分析。

#选取指定app数据进行下一步的分析
anaDf=filedf[filedf['resource']=='今日头条']
anaDf=anaDf.append(filedf[filedf['resource']=='百度百家'],ignore_index=True)
anaDf=anaDf.append(filedf[filedf['resource']=='UC头条'],ignore_index=True)
anaDf.info()
>>>
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 65532 entries, 0 to 65531
Data columns (total 8 columns):
title          65532 non-null object
author         65532 non-null object
num_read       65532 non-null int64
num_comment    65532 non-null int64
resource       65532 non-null object
type           65532 non-null object
pub_day        65532 non-null object
pub_hour       65532 non-null object
dtypes: int64(2), object(6)
memory usage: 4.0+ MB

六、分析数据

根据问题,进行数据分析。

6.1不同app之间横向对比用户热度及资讯特点

6.1.1不同app的用户热度

通过分析不同app的周资讯量、周作者量、平均每篇资讯的阅读量及评论量,来对比其用户参与度的差异。

#不同app上周资讯量以及周作者量(有推送资讯的作者只记一次)
print('不同app的周资讯量:\n',anaDf[['title','resource']].groupby(['resource']).count())
print('\n去重后不同app的作者量:\n',anaDf[['author','resource']].drop_duplicates().groupby(['resource']).count())
>>>
不同app的周发文量
           title
resource       
UC头条      30930
今日头条      26833
百度百家       7769

去重后不同app的作者量
           author
resource        
UC头条       10532
今日头条       12842
百度百家        5205

#不同app资讯平台作者量、发文量以及平均阅读量对比
fig = plt.figure(figsize=(10,6))
ax = fig.add_subplot(1,1,1)

infoWeeklySour.plot(style='b--',alpha=0.6,ax=ax)
avgRead.plot(style='-',ax=ax,color='g')
authorWeeklySour.plot(kind='bar',width=0.4,alpha=0.6,secondary_y=True,ax=ax)
ax.set_xlabel('resource')
ax.set_ylabel('Number of info')
ax.right_ax.set_ylabel('Number of author')
ax.set_title('The sum number of infos and authors in different resource',fontsize=15)

今日头条的内容创作者更多;百度百家上创作者数量及产出效率均较低;UC头条上创作者显得更高产,平均每位创作者每周发布近三篇资讯。

#以资讯title为一篇文章的唯一ID,平均每篇资讯的阅读量及评论量
#不同app相应的总阅读量
week_avg=anaDf[['num_read','resource']].groupby(['resource']).sum()
#不同app相应的总评论量
week_avg['num_comment']=anaDf[['num_comment','resource']].groupby(['resource']).sum()
#不同app相应的总资讯量
week_avg['num_title']=anaDf[['title','resource']].drop_duplicates().groupby(['resource']).count()
#得到不同app每篇资讯的平均阅读量及评论量
week_avg['avg_read']=round(week_avg['num_read']/week_avg['num_title'],1)
week_avg['avg_comment']=round(week_avg['num_comment']/week_avg['num_title'],2)
week_avg['com_read']=round(week_avg['avg_comment']/week_avg['avg_read']*100,3)
week_avg

虽然UC头条的资讯量更多,但今日头条的平均每篇资讯的阅读量及评论量会更高。此外,在今日头条上的资讯平均每100个阅读量就会有0.2条评论,而UC头条上仅有0.08条评论,这说明今日头条上的用户参与度可能更高。

6.1.2不同app的资讯特点

不同app上资讯量(即发文量)、平均阅读量以及平均评论量较多的资讯类型。

首先计算不同app上各类资讯的周阅读量、周评论量、周发文量,以及平均每篇资讯的阅读量和评论量。

#今日头条不同类型资讯的发文量、平均阅读量、平均评论量
info_toutiaoDf=filedf[['num_read','type']][filedf['resource']=='今日头条'].groupby(['type']).sum()
info_toutiaoDf['num_comment']=filedf[['num_comment','type']][filedf['resource']=='今日头条'].groupby(['type']).sum()
info_toutiaoDf['num_title']=filedf['type'][filedf['resource']=='今日头条'].value_counts()
info_toutiaoDf['avg_read']=round(info_toutiaoDf['num_read']/info_toutiaoDf['num_title'],2)
info_toutiaoDf['avg_comment']=round(info_toutiaoDf['num_comment']/info_toutiaoDf['num_title'],2)
info_toutiaoDf.head()
今日头条

用同样的方法,得到“趣头条”,“百度百家”,“UC头条”的相关指标:

趣头条
百度百家
UC头条

可视化各app资讯平台发文量前十二资讯类型

#可视化各app资讯平台发文量前十二资讯类型
toutiaoTopDf=info_toutiaoDf.sort_values('num_title',ascending=False).head(12)
quTopDf=info_quDf.sort_values('num_title',ascending=False).head(12)
baiduTopDf=info_baiduDf.sort_values('num_title',ascending=False).head(12)
ucTopDf=info_ucDf.sort_values('num_title',ascending=False).head(12)

#toutiao
avgReadtoutiaoTop=toutiaoTopDf.drop(['num_read','num_comment','num_title','avg_comment'],axis=1)
numTitletoutiaoTop=toutiaoTopDf.drop(['num_read','num_comment','avg_read','avg_comment'],axis=1)
#qu
avgReadquTop=quTopDf.drop(['num_read','num_comment','num_title','avg_comment'],axis=1)
numTitlequTop=quTopDf.drop(['num_read','num_comment','avg_read','avg_comment'],axis=1)
#baidu
avgReadbaiduTop=baiduTopDf.drop(['num_read','num_comment','num_title','avg_comment'],axis=1)
numTitlebaiduTop=baiduTopDf.drop(['num_read','num_comment','avg_read','avg_comment'],axis=1)
#uc
avgReaducTop=ucTopDf.drop(['num_read','num_comment','num_title','avg_comment'],axis=1)
numTitleucTop=ucTopDf.drop(['num_read','num_comment','avg_read','avg_comment'],axis=1)

fig=plt.figure(figsize=(16,8))
ax1=fig.add_subplot(2,2,1)
ax2=fig.add_subplot(2,2,2)

numTitletoutiaoTop.plot(kind='bar',alpha=0.6,ax=ax1)
avgReadtoutiaoTop.plot(style='-',alpha=0.6,ax=ax1,secondary_y=True)
plt.xticks(fontsize=12)
ax1.set_xticklabels(numTitletoutiaoTop.index)
ax1.set_ylabel('Total number of titles')
ax1.set_title('Toutiao',fontsize=15)

numTitlequTop.plot(kind='bar',alpha=0.6,ax=ax2)
avgReadquTop.plot(style='-',alpha=0.6,ax=ax2,secondary_y=True)
plt.xticks(fontsize=12)
ax2.right_ax.set_ylabel('Average number of read')
ax2.set_title('QU',fontsize=15)
plt.show()

fig=plt.figure(figsize=(16,8))
ax1=fig.add_subplot(2,2,1)
ax2=fig.add_subplot(2,2,2)

numTitlebaiduTop.plot(kind='bar',alpha=0.6,ax=ax1)
avgReadbaiduTop.plot(style='-',alpha=0.6,ax=ax1,secondary_y=True)
plt.xticks(fontsize=12)
ax1.set_ylabel('Total number of titles')
ax1.set_title('Baidu',fontsize=15)

numTitleucTop.plot(kind='bar',alpha=0.6,ax=ax2)
avgReaducTop.plot(style='-',alpha=0.6,ax=ax2,secondary_y=True)
plt.xticks(fontsize=12)
ax2.right_ax.set_ylabel('Average number of read')
ax2.set_title('UC',fontsize=15)
plt.show()

对这四款app发文量Top12的资讯类型进行分析:

1. 这四款app上发文量最多的资讯类型基本都是娱乐类资讯,今日头条和趣头条娱乐类资讯明显更多,UC头条娱乐、体育、科技咨询量较多,而百度百家各类资讯的发文量较为平均。

2. 发文量较多的资讯类别,其资讯平均阅读量不一定多,而且不同资讯平台上平均阅读量最多的资讯类型也有差异。今日头条上“国际”、“体育”类资讯用户关注量较高,趣头条及百度百家分别为“热点”、“体育”类资讯较受用户关注,UC头条上则为发文量最多的“娱乐”类资讯更受用户关注。

从平均阅读量和平均评论量两个角度看:

>>>
今日头条平均阅读量前五的资讯类型:
       num_title   avg_read  avg_comment
type                                   
小说           11  119784.00         7.91
时政          255   83907.91       231.33
国际          789   57230.97        86.01
历史          453   42676.92        70.87
体育          922   39331.88        90.93
>>>
今日头条平均评论量前五的资讯类型:
       num_title  avg_read  avg_comment
type                                  
时政          255  83907.91       231.33
文化          432  13829.05       124.25
社会         1631  22107.72       121.55
体育          922  39331.88        90.93
军事          648  35566.68        88.28

与发文量的结果进行对比,平均阅读量高的资讯类型发文量不一定多。例如今日头条中的“小说”类型,可能其发文数量不多,但会引起读者反复的点击或者阅读。

对比不同app资讯平台,今日头条平均评论量Top12资讯类型的平均阅读量及平均评论量都很高,说明其用户参与度较高。

#今日头条“时政”类资讯举例
filedf[(filedf['resource']=='今日头条')&(filedf['type']=='时政')].sort_values(by='num_comment',ascending=False).head()

6.2今日头条不同时段对比用户热度

6.2.1不同周期的用户热度

通过分析不同周期发文量、周作者量、平均每篇资讯的阅读量及评论量,来对比其用户参与度的差异。

#先将发布日期转为时间类型
anaDf['date_formatted']=pd.to_datetime(anaDf['pub_day'],format='%m/%d/%Y')
#再求相应时间的周期
anaDf['weekday']=anaDf['date_formatted'].map(lambda x:x.weekday_name)
#选择今日头条的数据进一步分析
toutiaoDf=anaDf[anaDf['resource']=='今日头条']
toutiaoDf.head()
toutiaoDf.groupby(['weekday']).describe()

通过分析不同周期阅读量及评论量的描述统计性信息,我们发现周期间确实存在较大差异,但没有明显的数据异常情况。

#提取数据
toutiaoWeekDf=toutiaoDf.groupby(['weekday']).describe().reindex(['Monday','Tuesday','Wednesday','Thursday',
                                                   'Friday','Saturday','Sunday'])
weekavgreadDf=toutiaoWeekDf['num_read']['mean']
weekavgcomDf=toutiaoWeekDf['num_comment']['mean']

#作图-线性图
fig=plt.figure(figsize=(10,5))
ax=fig.add_subplot(1,1,1)
weekavgreadDf.plot(kind='bar',alpha=0.8)
weekavgcomDf.plot(style='.-',alpha=0.8,secondary_y=True)
ax.set_ylabel('The average number of reading')
ax.right_ax.set_ylabel('The average number of comment')
plt.title('The average number of reading and comment per weekday',fontsize=15)
#求不同周期发文量、作者量平均每篇资讯的阅读量及评论量
weekdayDf=toutiaoDf[['weekday','num_read']].groupby(['weekday']).sum()
weekdayDf['num_comment']=toutiaoDf[['weekday','num_comment']].groupby(['weekday']).sum()
weekdayDf['num_title']=toutiaoDf[['weekday','title']].groupby(['weekday']).count()
weekdayDf['num_author']=toutiaoDf[['weekday','author']].drop_duplicates().groupby(['weekday']).count()
weekdayDf['avg_read']=round(weekdayDf['num_read']/weekdayDf['num_title'],2)
weekdayDf['avg_comment']=round(weekdayDf['num_comment']/weekdayDf['num_title'],2)
weekdayDf['title/author']=round(weekdayDf['num_title']/weekdayDf['num_author'],2)
weekdayDf.sort_values(by='num_title',ascending=False)
  1. 一周中,几乎所有指标在周一达到最低值,说明周一作者发布内容或用户浏览资讯的意愿均较低;
  2. 资讯量一般周一、周二较低,这也和内容创作者在这两天也较少发布新资讯相符合;
  3. 总阅读量在周四达到峰值,但平均阅读量在周二达到峰值,这可能是因为相对于阅读量的降幅,资讯量的降幅更大;
  4. 总评论量以及人均评论量在周五均达到峰值,可能是因为周五临近周末,大家更愿意刷手机浏览信息并参与话题讨论。

6.2.2不同时刻的用户热度

通过分析不同时刻发文量、周作者量、平均每篇资讯的阅读量及评论量,来对比其用户参与度的差异。

#求不同时刻发文量、作者量平均每篇资讯的阅读量及评论量
hourDf=toutiaoDf[['pub_hour','num_read']].groupby(['pub_hour']).sum()
hourDf['num_comment']=toutiaoDf[['pub_hour','num_comment']].groupby(['pub_hour']).sum()
hourDf['num_title']=toutiaoDf[['pub_hour','title']].groupby(['pub_hour']).count()
hourDf['num_author']=toutiaoDf[['pub_hour','author']].drop_duplicates().groupby(['pub_hour']).count()
hourDf['avg_read']=round(hourDf['num_read']/hourDf['num_title'],2)
hourDf['avg_comment']=round(hourDf['num_comment']/hourDf['num_title'],2)
hourDf['title/author']=round(hourDf['num_title']/hourDf['num_author'],2)
hourDf.sort_values(by='avg_read',ascending=False)
#取数据
houravgreadDf=hourDf['avg_read']
houravgcomDf=hourDf['avg_comment']

#画图
fig=plt.figure(figsize=(10,5))
ax=fig.add_subplot(1,1,1)
houravgreadDf.plot(kind='bar',alpha=0.8)
houravgcomDf.plot(style='.-',alpha=0.8,secondary_y=True)
ax.set_ylabel('The average number of reading')
ax.right_ax.set_ylabel('The average number of comment')
plt.title('The average number of reading and comment per hour',fontsize=15)
  1. 凌晨1点至5点,发布资讯量以及作者量均较低,但是在此时段所发布资讯的平均阅读量以及评论量(尤其是凌晨2、3点)都很高,这也有可能是因为某几篇资讯的阅读量和评论量异常拉高所致(此时段文章量较少容易被异常数据影响);
  2. 上午10点及11点资讯发布量最高,但在晚上12点以及凌晨6点,平均每位作者发布的资讯量较高,达1.5篇/人以上;
  3. 剔除凌晨时段后,平均阅读量和平均评论量在下午6点和上午11点较高,说明人们在这两个时间段浏览资讯并参与话题的意愿较强。

七、得出启示

浏览者角度:

  1. 今日头条和UC头条上资讯类型更加多样化,更易获取除了娱乐资讯外的资讯;
  2. 趣头条和今日头条上内容创作者和资讯量更多,可以提供更多内容以便浏览。

创作者角度:

  1. 在每天早上11点或下午6点前后,浏览者浏览资讯并参与话题的意愿较强,因此如果在此时段前发布内容有利于增加自己文章的曝光量;
  2. 不同周期浏览者浏览意愿不同,应尽可能选择在用户浏览意愿更强的时候发布资讯,如周四、周五及周六。

八、反思&计划

可能存在的问题:

  1. 数据抓#取的不稳定性导致可能有部分数据缺失;
  2. 结果展示形式单一,应注意选择合适的方式进行数据可视化。

下阶段分析及改进方向:

  1. 寻找更稳定的数据抓#取工具或者数据源;
  2. 探索更多维度:如内容创作者所发布资讯类型的多少与其阅读量、评论量之间是否有关系?即专注于某领域的内容创作者和涉猎范围较广的内容创作者,哪个更容易获取更多的流量;以及用户生命周期管理视角下,不同类型用户留存以及转化率情况等。

ps.本文最初也有受 @李少(四声) 启发,这篇如何将数据导入Mysql也较详细。

此外附上小编的CSDN学习园地,会记录和分享一些Python和数据库学习总结,希望多多交流。

编辑于 2021-11-11 15:54