参考网址:https://www.jianshu.com/p/17e264a08555
背景及目的
一帮对数据分析感兴趣的人组成了学习小组,计划每周总结一篇文章,提交到简书专栏。截止到2月24号,本专栏已成立11周,现在通过爬取专栏数据,来总结小组成员提交作业情况。
1、数据爬取
字段:
name:作者
title:文章标题
word_age:文章字数(防止有同学敷衍了事)
publish_time: 文章发布时间(带*的表示文章后来编辑过)
comments_count:评论数量
likes_count:喜欢数
工具:Python中的requests+Beautifulsoup+re库
思路:从首页爬取文章标题、地址、评论数和点赞数,从文章详情页爬取作者、发布时间和文章字数
结果:保存到aa库中的exercises表中
import requests
from bs4 import BeautifulSoup
import pymysql
from requests.exceptions import RequestException
import re
# import json
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 ' +
'(KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36'
}
def get_page_index(number):
url = 'https://www.jianshu.com/c/af12635a5aa3?order_by=added_at&page=%s' % number
try:
responses = requests.get(url, headers=headers)
if responses.status_code == 200:
return responses.text
return None
except RequestException:
print('请求有错误')
return None
def get_page_detail(url):
try:
responses = requests.get(url, headers=headers)
if responses.status_code == 200:
return responses.text
return None
except RequestException:
print('请求详情页有错误')
return None
def parse_detail_page(title, html): # 获得标题和链接后,分析每一页的内容,获得发布时间和文章字数
title = title
soup = BeautifulSoup(html, 'html.parser')
name = soup.find_all('div', class_='info')[0].find_all('span', class_='name')[0].find_all('a')[0].contents[0]
content_detail = soup.find_all('div', class_='info')[0].find_all('div', class_='meta')[0]
# print(content_detail)
# content_detail = [info.contents[0] for info in content_detail] # 时间、字数、评论、喜欢
publish_time = content_detail.find_all('span', 'publish-time')[0].contents[0]
word_age = content_detail.find_all('span', 'wordage')[0].contents[0]
return name, title, word_age, publish_time
def parse_index_page(html):
soup = BeautifulSoup(html, 'html.parser')
note_list = soup.find_all(name='ul', class_='note-list')[0]
content_li = note_list.find_all(name='li')
dir = {}
for link in content_li:
# url = link.find_all('a', class_='title')[0]
# title = url.contents[0] # 获取每篇文章的标题,并去除标签格式
# dir[link] = title
url = link.find_all('a', class_='title')[0]
linknew = 'https://www.jianshu.com/' + url.get('href') # 获取每篇文章的链接
title = url.contents[0] # 获取每篇文章的标题,并去除标签格式
pattern = re.compile(
'<li.*?class="meta">.*?<i.*?ic-list-comments"></i>(.*?)\n*</a>.*?ic-list-like"></i>(.*?)</span>.*?</li>', re.S)
result = re.findall(pattern, str(link))
comments_count = result[0][0]
likes_count = result[0][1]
dir[linknew] = [title, comments_count, likes_count]
# print(title, comments_count, likes_count)
return dir
def save_to_mysql(name, title, word_age, publish_time, comments_count, likes_count):
conn = pymysql.connect(host='localhost', user='root', password='123', db='aa', port=3306, charset='utf8')
cur = conn.cursor()
insert_data = 'insert into exercises(name, title, word_age, publish_time,comments_count,' \
'likes_count)''values(%s, %s, %s, %s, %s, %s)'
val = [name, title, word_age, publish_time, comments_count, likes_count]
cur.execute(insert_data, val)
conn.commit()
conn.close()
def write_to_file(name, title, word_age, publish_time, comments_count, likes_count):
content = name + ' ' + title + ' ' + word_age + ' ' + publish_time + ' ' + comments_count + ' ' +likes_count
with open('datafrog.txt', 'a', encoding='utf-8') as f:
f.write(content + '\n')
f.close()
def main():
for number in range(1, 53):
html = get_page_index(number) # 网页源码
dir = parse_index_page(html) # 解析网页源码,得到文章标题和链接
for link, values in dir.items():
# print(link)
html = get_page_detail(link)
# print(html)
comments_count, likes_count = values[1], values[2]
name, title, word_age, publish_time = parse_detail_page(values[0], html)
save_to_mysql(name, title, word_age, publish_time, comments_count, likes_count)
write_to_file(name, title, word_age, publish_time, comments_count, likes_count)
if __name__ == '__main__':
main()
读取数据
import pymysql
import pandas as pd
import numpy as np
import matplotlib as plt
%matplotlib inline
conn = pymysql.connect(host='localhost', user='root', password='123', db='aa', port=3306, charset='utf8')
sql = 'select * from exercises'
df = pd.read_sql(sql, conn)
del df['id']
df.head()
结果
image.png
2、数据处理
查看数据总体信息
df.info()
返回
image.png
- 将字数字段中的'字数'清除,修改为int类型
df['word_age'] = df['word_age'].apply(lambda x:x[3:])
df['word_age'] = df['word_age'].astype(int)
- 删除publish_time字段中的*号,修改为datetime类型
df['publish_time'] = df['publish_time'].str.strip('*')
df['publish_time'] = pd.to_datetime(df['publish_time'])
- 修改字段comments_count、likes_count为int类型
df['comments_count'] = df['comments_count'].astype('int')
df['likes_count'] = df['likes_count'].astype('int')
- 将发布时间定位在2019年2月24号之前,正好一周
df = df.loc[~(df['publish_time']>='2019-02-25')]
df.index = list(range(0,df.shape[0]))
3、数据分析
-
文章篇数
image.png
grouped_name = df.groupby('name')
grouped_name.count().sort_values(by='title',ascending=False).head()
返回
image.png
grouped_name.count().min()
返回
image.png
grouped_name.count().sort_values(by='title',ascending=False).apply(lambda x:x.cumsum()/x.sum()).reset_index()['title'].plot()
返回
image.png
grouped_name.count().reset_index()['title'].plot.hist()
返回
小结:
一共有74为简书作者向专栏投稿,共计516篇;
平均每人投稿7篇,截止到第11周,说明有部分同学少交了几次作业;
提交文章数最多为25篇,最少为1篇;
提交文章数排名前五的用户为Spareribs、1点点De小任性丶、夜希辰、凡人求索、蜗牛上高速c;
排名前1/3的用户贡献了60%的文章数,有点二八原则的趋势;
约1/3的用户的投稿量少于三篇。
-
文章字数
image.png
竟然有写了十多个字,甚至还有0个的,选了几个点进去看了一下,这些文章大都是以图片形式呈现的,所以不算在字数里面,删除这些字数为0的计算平均值。
image.png
grouped_name.sum().sort_values(by='word_age',ascending=False).head()
返回
image.png
grouped_name.sum().sort_values(by='word_age',ascending=False).head()
返回
image.png
grouped_name.sum().sort_values(by='word_age',ascending=False).apply(lambda x:x.cumsum()/x.sum()).head(15)['word_age']
返回
小结:
小组成员总计写了411305个字,平均每人写了5539个字,最多的写了28803字;
部分文章以图片形式呈现,字数比较少,实际上小组成员的总结并不止这41万字;
字数排名前五的用户为夜希辰、王阿根、凡人求索、怀柔小龙虾、Spareribs,文章最多的并不代表字数最多,这些用户的每篇总结都很详细;
字数排名前十五的用户贡献了55%的字数,大家都很努力啊!
-
评论数
image.png
comment = df.pivot_table(index='name',values=['comments_count','title'],aggfunc={'comments_count':'sum','title':'count'})
head_comment = comment.sort_values(by='comments_count',ascending=False).head()
head_comment
返回
image.png
df.sort_values(by='comments_count',ascending=False).head(1)
返回
image.png
image.png
小结:
总计获得225条评论,平均每篇文章获得3条评论,这里面还包括本人回复的,评论不是很多;
评论数排在前五的用户为夜希辰、凡人求索、Spareribs、小佳数据分析、Lykit01,前两位的平均评论可以达到每篇两条;
评论数最高的文章是专栏的第一篇文章:关于0基础入门数据分析的,大家果然都是奔着转行去的;
约75%的用户总评论数小于等于3条,其中包括大量0评论用户。
小组成员之间的互动还是比较低的。
-
喜欢数
image.png
df.sort_values(by='likes_count',ascending=False).head(1)
返回
image.png
grouped_name.sum().sort_values(by='likes_count',ascending=False).head()
返回
image.png
小结:
总计获得1501个赞,平均每人获赞20个;
点赞数最多的文章是可视化神器--Plotly,共获得152个赞,贡献了约10%的点赞数;
点赞数排名前五的用户分别为Spareribs、凡人求索、estate47、夜希辰、1点点De小任性丶,用户estate47写的字不是很多,但是凭借可视化神器--Plotly一文拔得头筹。
- 发布时间
df['hour'] = df['publish_time'].apply(lambda x:x.hour)
plt.rcParams['figure.figsize'] = (9,5)
df['hour'].value_counts().plot.bar()
返回
image.png
小结:
大部分同学都在下午16点之后或者中午11点提交作业,其中晚上19点是提交的高峰期;
凌晨1点到早上7点,偶尔会有同学提交作业。
- 每周提交情况
df['year'] = df['publish_time'].apply(lambda x:x.isocalendar()[0])
df['week'] = df['publish_time'].apply(lambda x:x.isocalendar()[1])
df.groupby(['year','week']).count()['name']
返回
image.png
第一篇投稿时间是2018年的第49周,但是有两篇2018年39周和48周的文章,猜测原因在于这些作者将之前的文章投稿到了专栏,此处将这两篇的投稿时间设为前一篇文章的投稿时间
df.loc[df['week']==39,['year','week']] = np.NaN
df.loc[df['week']==48,['year','week']] = np.NaN
df = df.fillna(method='bfill')
df.groupby(['year','week']).count()['name'].plot()
返回
image.png
图中的第一周应该不是正式开始的第一周吧?群主发布学习计划,算个预热,投稿文章不多。
- 看一下小组成员提交文章的时间间隔
df.groupby('name').apply(lambda x: x['week']-x['week'].shift(-1)).replace({-51.0:1.0,-50.0:2.0,-49.0:3.0,-1.0:0.0}).value_counts().plot.bar()
返回
image.png
小结:
刚开始投稿比较积极,后面有些疲软,特别是春节期间,投稿量大幅下降;
投稿最多的时候能达到80篇,现在的投稿量只能达到当时的一半,部分同学未能坚持投稿;
大部分同学的投稿是1周,投稿周期最长可达4周,部分同学一周会上传多篇文章。
- 看看哪些用户有在11周内提交至少11文章呢?
name_gp = df.groupby('name').count()
name_gp.loc[name_gp['title']>=11].reset_index()
返回
image.png
everyweek_name = name_gp.loc[name_gp['title']>=11].reset_index()['name']
df.loc[df['name'].isin(everyweek_name)].groupby('name')['week'].nunique()
返回
image.png
小结:
用户1点点De小任性丶、Greatsmile、Lykit01、Spareribs、cynthia猫、estate47、yimengtianya1、凡人求索、夜希辰、小佳数据分析、张叁疯、王阿根、蜗牛上高速c在前11周内都提交了至少11篇文章,其中大部分用户均有一周提交多篇文章的行为
- 每周最受欢迎的作者和文章,受欢迎度=0.2 * 文章字数+0.2 * 评论数+0.6 * 点赞数
def get_popularity(x):
score = 0.2*x['word_age'] + 0.2*x['comments_count'] + 0.6*x['likes_count']
return score
df['score'] = df.apply(get_popularity,axis=1)
df.groupby(['year','week']).apply(lambda x: x.loc[x['score'].idxmax()])['title']
返回
image.png
每周最受欢迎文章涵盖了数据分析师所要具备的多种技能
grouped_week = df.groupby(['year','week','name'])
score_week = grouped_week.sum().reset_index()
best_score_week = score_week.groupby(['year','week']).apply(lambda x: x.loc[x['score'].idxmax()])
best_score_week['name'].value_counts()
返回
image.png
小结:
凡人求索、王阿根和夜希辰多次获得周最受欢迎作者。
学习小组正式成立之后,投稿量先上升再下降,刚开始大家还是很积极的,后面有点坚持不下去;
过年那段时间投稿量大幅下降,最近渐渐有在回升,过完年,大家开始慢慢回到学习上了;
用户凡人求索、王阿根和夜希辰多两次获得周最受欢迎作者;
每周最受欢迎的文章包括数据分析常用库的使用、业务知识、统计学、机器学习、爬虫等。
总结
1.截至2019.2.24日,,一共有74位小组成员提交了共516篇文章。
2.一天中提交作业的时段在16点之后或者中午11点,19点是高峰期。
3.平均每位小组成员发布了7篇文章。最多的已发布25篇。
4.投稿量最多的时候一周能达到80篇,现在投稿量仅为一半,部分同学未能坚持投稿。
5.大部分同学的投稿周期是1周,投稿周期最长可达4周,部分同学一周会上传多篇文章。
6.平均写作字数5539个。最多的累积写作字数达到28803个。
7.小组成员之间的互动较低,成员更愿意点赞,较少发表评论。
8.用户夜希辰、王阿根和凡人求索多次获得周最受欢迎作者。
9.比较受欢迎的文章涵盖了数据分析师所要具备的多种技能。
网友评论