Python爬虫实战(2)数据爬取、绘图、词云、分析

前言

  • 蛋肥通过Python爬虫获取豆瓣电影TOP250数据的练习,掌握了爬虫的基本知识,然后蛋肥又去拜读了很多高手的爬虫实例,发现自己在最后的数据分析上实在乏善可陈,所以这一次尝试将更多的时间用在数据分析上,看能否得出一些有趣的信息。

准备

爬取时间:2020/11/26
系统环境:Windows 10
所用工具:Jupyter Notebook\Python 3.0
涉及的库:requests\lxml\pandas\matplotlib\datetime\jieba\stylecloud

获取基础数据

蛋肥想法:为了获取更多的信息,蛋肥打算先将产品运营分类下所有文章的网址爬取下来,然后再进入文章页面,爬取题目、作者、评论等详细信息。

产品运营|人人都是产品经理
http://www.woshipm.com/category/operate
参考资料
用python的xpath和requests库爬取图片超详细实例
如何在python中把两个列表的各项分别合并为列表
Max retries exceeded with url问题解决
requests关于Exceeded 30 redirects问题得出的结论

爬取产品运营分类下所有文章的网址

import requests
from lxml import etree

#爬取产品运营分类下所有文章的网址
def gethref():
    href=[]
    #伪造请求头
    headers={"User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0"}
    #循环解决翻页问题
    for i in range(1,863):
        link="http://www.woshipm.com/category/operate/page/"+str(i)
        r=requests.get(link,headers=headers,timeout=10)
        print(str(i),r.status_code)
        #爬取对应xpath下的数据并存入列表
        html=etree.HTML(r.text)
        href_t=html.xpath('//h2[@class="post-title"]/a/@href')
        href.extend(href_t)
    return(href)

#执行函数
href=gethref()

爬取文章详细信息

#爬取文章详细信息
def getinfo(list):
    info=[]
    #伪造请求头
    headers={"User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0"}
    #遍历每个链接
    for i in range(len(list)):
        link=list[i]
        #增加重试连接次数
        requests.DEFAULT_RETRIES=5
        #关闭多余连接
        s=requests.session()
        s.keep_alive=False
        r=requests.get(link,headers=headers,timeout=300,allow_redirects=False)
        print(str(i),r.status_code)
        #依次爬取题目、作者、发布时间、评论数、浏览量、收藏数、预计阅读时间、作者头像
        html=etree.HTML(r.text)
        title=html.xpath('//h2[@class="article--title"]/text()')
        author=html.xpath('//div[@class="author u-flex"]/a/text()')
        time=html.xpath('//div[@class="meta--sup"]/time/text()')
        comment=html.xpath('//div[@class="meta--sup__right"]/text()[1]')
        read=html.xpath('//div[@class="meta--sup__right"]/text()[2]')
        collect=html.xpath('//div[@class="meta--sup__right"]/text()[3]')
        lenth=html.xpath('//div[@class="meta--sup__right"]/el-tooltip/span/text()')
        pic=html.xpath('//div[@class="u-flex0"]/a/img/@src')
        info_t=[[a,b,c,d,e,f,g,h] for a,b,c,d,e,f,g,h in zip(title,author,time,comment,read,collect,lenth,pic)]
        info.extend(info_t)
    return(info)

#执行函数,如果执行时老是崩,可以分段执行最后拼接(蛋肥就是这样做的,只是为了代码好看没写出来)
data=getinfo(href)

数据预处理

蛋肥想法:经观察数据整体问题不大,检查缺失、去除空格、转化数据格式,最后将数据保存为xlsx。

小插曲
len(href)为10335,len(data)为10334,找了半天,才发现有一个文章禁止访问了,不知道因为啥原因,于是蛋肥便抛弃了它。

from datetime import datetime
#去除空格及不需要的字符,调整格式
for i in range(len(data)):    
    data[i]=[x.replace("\n","").replace(" ","") for x in data[i]]
    data[i][2]=datetime.strptime(data[i][2],'%Y-%m-%d')
    data[i][3]=int(data[i][3].replace("评论",""))
    data[i][5]=int(data[i][5].replace("收藏",""))
    data[i][6]=int(data[i][6].replace("分钟",""))
    #因浏览量存在过万的情况,如"1.2万",做一下转换
    if("万"in data[i][4]):
        data[i][4]=int(float(data[i][4].replace("浏览","").replace("万",""))*10000)
    else:
        data[i][4]=int(data[i][4].replace("浏览",""))

import pandas as pd
#保存数据
df=pd.DataFrame(data,columns=["题目","作者","日期","评论数","浏览数","收藏数","时长","作者头像"])
df.info()
df.to_excel(r"C:\Users\Archer\Desktop\爬取数据.xlsx",index=False)
Python爬虫实战(2)数据爬取、绘图、词云、分析_第1张图片
保存到本地的部分数据

数据可视化

蛋肥想法:数据相关图贴到数据分析中,此处只记录绘图代码,如想直接看分析,建议疯狂下滑。

import matplotlib.pyplot as plt

#画图四件套:显示、矢量、中文、负号
%matplotlib inline
%config InlineBackend.figure_format="svg"
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus']=False

绘制季度发文量变化趋势

参考资料
Python将数据框中的每日数据汇总到每月和每季度
python绘图保存的图像坐标轴显示不全以及图片周围空白较大的问题

#绘制季度发文量变化趋势
#为了不影响原数据表,重新建一个来操作数据
df_num=df.copy()
df_num.set_index("日期",inplace=True)
x=list(df_num.resample('QS').count().index)
y=list(df_num.resample('QS').count()["题目"])

#开始绘图
plt.figure(figsize=(12,5))
plt.subplot(1,1,1)
plt.plot(x,y)

#设置数据标签
for a,b in zip(x,y):
    plt.text(a,b,b,ha="center",va="bottom",fontsize=10)
#设置其他
plt.title("季度发文量变化趋势",fontsize=15)
plt.xticks(x,rotation=90)

#保存图片
plt.savefig(r"C:\Users\Archer\Desktop\季度发文量变化趋势.png",bbox_inches="tight")

绘制作者(发文总量>90)发文量变化趋势

小提示
所有作者发文量变化趋势,在下面的代码中去除筛选条件并做些许修改即可,故不另贴代码
参考资料
python如何水平显示图例元素

#筛选发文总量>90的作者
df_90=df.groupby("作者").count().sort_values("题目",ascending=False)
df_90=df_90[df_90["题目"]>90]
#将作者数据存入列表
author=list(df_90.index)

#开始绘图,因为老曹前期发布了大量文章,为了不影响其他数据显示,设置y轴范围(0,120)
plt.figure(figsize=(12,5))
plt.subplot(1,1,1)
plt.title("发文总量>90的作者发文量变化趋势",fontsize=15)
plt.ylim(0,120)

#循环绘制每一个作者的趋势曲线
for i in range(len(author)):
    df_author=df[df["作者"]==author[i]].copy()
    df_author.set_index("日期",inplace=True)
    x=list(df_author.resample('QS').count().index)
    y=list(df_author.resample('QS').count()["题目"])
    plt.plot(x,y,label=author[i])
    
#与【季度发文量变化趋势】统一横坐标轴
date=list(df_num.resample('QS').count().index)
plt.xticks(date,rotation=90)

#添加横向图例
plt.legend(loc="upper left",ncol=8)

#保存图片
plt.savefig(r"C:\Users\Archer\Desktop\作者发文量变化趋势.png",bbox_inches="tight")

绘制发文量排行榜

#绘制发文量排行榜
from datetime import datetime
#为了不影响原数据表,重新建一个来操作数据
df_r=df.copy()
df_r.set_index("日期",inplace=True)

#绘制画布
plt.figure(figsize=(14,18))

#遍历每一年的排行榜
for i in range(0,9):
    df_m=df_r[(df_r.indexdatetime(2012+i,1,1))].groupby("作者").count().sort_values("题目")[-5:]
    x=list(df_m.index)
    y=list(df_m["题目"])
    plt.subplot(5,2,i+1)
    plt.barh(x,y)
    plt.title(str(2012+i)+"年发文量TOP5",fontsize=15)
    #添加数据标签
    for a,b in zip(x,y):
        plt.text(b,a,b,ha="left",va="center",fontsize=10)
    #隐藏xticks,节约空间
    plt.xticks([])

#保存图片
plt.savefig(r"C:\Users\Archer\Desktop\发文量排行榜.png",bbox_inches="tight")

文章互动总数TOP10系列

#文章-浏览总数TOP10
df_read=df.sort_values("浏览数",ascending=False)[0:10].iloc[:,[0,1,2,4]]
#文章-评论总数TOP10
df_comment=df.sort_values("评论数",ascending=False)[0:10].iloc[:,[0,1,2,3]]
#文章-收藏总数TOP10
df_collect=df.sort_values("收藏数",ascending=False)[0:10].iloc[:,[0,1,2,5]]

作者互动总数TOP10系列

#作者-浏览总数TOP10
df_a_read=df.groupby("作者").aggregate({"浏览数":"sum"}).sort_values("浏览数")[-10:]
#作者-评论总数TOP10
df_a_comment=df.groupby("作者").aggregate({"评论数":"sum"}).sort_values("评论数")[-10:]
#作者-收藏总数TOP10
df_a_collect=df.groupby("作者").aggregate({"收藏数":"sum"}).sort_values("收藏数")[-10:]

#绘制画布
plt.figure(figsize=(12,15))
#绘制作者-浏览总数TOP10
plt.subplot(3,1,1)
plt.title("作者-浏览总数TOP10",fontsize=15)  
x=list(df_a_read.index)
y=list(df_a_read["浏览数"])
plt.barh(x,y)
for a,b in zip(x,y):
    plt.text(b,a,b,ha="left",va="center",fontsize=10)
#隐藏xticks,节约空间
plt.xticks([])

#绘制作者-评论总数TOP10
plt.subplot(3,1,2)
plt.title("作者-评论总数TOP10",fontsize=15)  
x=list(df_a_comment.index)
y=list(df_a_comment["评论数"])
plt.barh(x,y)
for a,b in zip(x,y):
    plt.text(b,a,b,ha="left",va="center",fontsize=10)
#隐藏xticks,节约空间
plt.xticks([])

#绘制作者-收藏总数TOP10
plt.subplot(3,1,3)
plt.title("作者-收藏总数TOP10",fontsize=15)  
x=list(df_a_collect.index)
y=list(df_a_collect["收藏数"])
plt.barh(x,y)
for a,b in zip(x,y):
    plt.text(b,a,b,ha="left",va="center",fontsize=10)
#隐藏xticks,节约空间
plt.xticks([])

#保存图片
plt.savefig(r"C:\Users\Archer\Desktop\作者互动排行榜.png",bbox_inches="tight")

作者互动平均数TOP10系列

python中关于round函数的小坑

#作者-浏览平均数TOP10
df_a_read_mean=df.groupby("作者").aggregate({"浏览数":"sum","题目":"count"}).sort_values("浏览数")
df_a_read_mean.insert(2,"平均",df_a_read_mean["浏览数"]//df_a_read_mean["题目"])
df_a_read_mean=df_a_read_mean[df_a_read_mean["题目"]>10].sort_values("平均")[-10:]
#作者-评论平均数TOP10
df_a_comment_mean=df.groupby("作者").aggregate({"评论数":"sum","题目":"count"}).sort_values("评论数")
df_a_comment_mean.insert(2,"平均",df_a_comment_mean["评论数"]//df_a_comment_mean["题目"])
df_a_comment_mean=df_a_comment_mean[df_a_comment_mean["题目"]>10].sort_values("平均")[-10:]
#作者-收藏平均数TOP10
df_a_collect_mean=df.groupby("作者").aggregate({"收藏数":"sum","题目":"count"}).sort_values("收藏数")
df_a_collect_mean.insert(2,"平均",df_a_collect_mean["收藏数"]//df_a_collect_mean["题目"])
df_a_collect_mean=df_a_collect_mean[df_a_collect_mean["题目"]>10].sort_values("平均")[-10:]

#绘制画布
plt.figure(figsize=(12,15))
#绘制作者-浏览平均数TOP10
plt.subplot(3,1,1)
plt.title("作者-浏览平均数TOP10",fontsize=15)  
x=list(df_a_read_mean.index)
y=list(df_a_read_mean["平均"])
plt.barh(x,y)
for a,b in zip(x,y):
    plt.text(b,a,b,ha="left",va="center",fontsize=10)
#隐藏xticks,节约空间
plt.xticks([])

#绘制作者-评论平均数TOP10
plt.subplot(3,1,2)
plt.title("作者-评论平均数TOP10",fontsize=15)  
x=list(df_a_comment_mean.index)
y=list(df_a_comment_mean["平均"])
plt.barh(x,y)
for a,b in zip(x,y):
    plt.text(b,a,b,ha="left",va="center",fontsize=10)
#隐藏xticks,节约空间
plt.xticks([])

#绘制作者-收藏平均数TOP10
plt.subplot(3,1,3)
plt.title("作者-收藏平均数TOP10",fontsize=15)  
x=list(df_a_collect_mean.index)
y=list(df_a_collect_mean["平均"])
plt.barh(x,y)
for a,b in zip(x,y):
    plt.text(b,a,b,ha="left",va="center",fontsize=10)
#隐藏xticks,节约空间
plt.xticks([])

#保存图片
plt.savefig(r"C:\Users\Archer\Desktop\作者互动平均排行榜.png",bbox_inches="tight")

绘制标题热词词云(以2020年为例)

参考资料
一款高颜值的词云包让我拍案叫绝
Python分词云图:中英文Stylecloud调用代码精校,可拿来直接用
Tableau Palettes

import jieba
from stylecloud import gen_stylecloud

#筛选2020年的标题,简单去掉中文停用词,建议去网上下更全的词表
df_c=df[(df["日期"]datetime(2020,1,1))]
textc=list(df_c["题目"])
textstop=["的","如何","是","与","和","你","从","怎么","到","做","什么","了","个","在","好"]
for i in range(len(textc)):
    for j in range(len(textstop)):
        textc[i]=textc[i].replace(textstop[j],"")

#保存成txt
file=open(r"C:\Users\Archer\Desktop\2020题目.txt","a+",encoding='utf-8')
for i in range(len(textc)):
    s=str(textc[i])
    file.write(s)
file.close()

#直接复制词云代码,icon_name对应词云轮廓,palette对应配色
def jieba_cloud(file_name):
    with open(file_name,'r',encoding='utf8') as f:
        word_list = jieba.cut(f.read())
        result = " ".join(word_list)
        #制作中文云词
        gen_stylecloud(text=result,palette='tableau.BlueRed_6',icon_name='fas fa-comment',font_path='C:\\Windows\\Fonts\\simhei.ttf',output_name=file_name.split('.')[0] + '.png')       
if __name__ == "__main__":
    file_name = r"C:\Users\Archer\Desktop\2020题目.txt"
    jieba_cloud(file_name)

题目长度相关性

df_title=df.copy()
#新增列记录题目长度
df_title["题目长度"]=df_title['题目'].str.len()
df_title_c=df_title.groupby("题目长度").count()

#绘制画布
plt.figure(figsize=(12,20))
#绘制题目长度分布
plt.subplot(4,1,1)
#设置横纵坐标轴
plt.xlabel("题目长度")
plt.ylabel("数量")
#设置标题
plt.title("题目长度 分布")
#绘制分布图
plt.bar(df_title_c.index,df_title_c["题目"])

#绘制题目长度-浏览数相关性
plt.subplot(4,1,2)
#设置横纵坐标轴
plt.xlabel("题目长度")
plt.ylabel("浏览数")
#设置标题
plt.title("题目长度-浏览数 相关性")
#绘制散点图
plt.scatter(df_title["题目长度"],df_title["浏览数"])

#绘制题目长度-评论数相关性
plt.subplot(4,1,3)
#设置横纵坐标轴
plt.xlabel("题目长度")
plt.ylabel("评论数")
#设置标题
plt.title("题目长度-评论数 相关性")
#绘制散点图
plt.scatter(df_title["题目长度"],df_title["评论数"])

#绘制题目长度-收藏数相关性
plt.subplot(4,1,4)
#设置横纵坐标轴
plt.xlabel("题目长度")
plt.ylabel("收藏数")
#设置标题
plt.title("题目长度-收藏数 相关性")
#绘制散点图
plt.scatter(df_title["题目长度"],df_title["收藏数"])

#保存图片
plt.savefig(r"C:\Users\Archer\Desktop\题目长度相关性.png",bbox_inches="tight")

文章时长相关性

df_time=df.copy()
df_time_c=df_time.groupby("时长").count()

#绘制画布
plt.figure(figsize=(12,20))
#绘制文章时长分布
plt.subplot(4,1,1)
#设置横纵坐标轴
plt.xlabel("文章时长")
plt.ylabel("数量")
#设置标题
plt.title("文章时长 分布")
#绘制分布图
plt.bar(df_time_c.index,df_time_c["题目"])

#绘制文章时长-浏览数相关性
plt.subplot(4,1,2)
#设置横纵坐标轴
plt.xlabel("文章时长")
plt.ylabel("浏览数")
#设置标题
plt.title("文章时长-浏览数 相关性")
#绘制散点图
plt.scatter(df_time["时长"],df_time["浏览数"])

#绘制文章时长-评论数相关性
plt.subplot(4,1,3)
#设置横纵坐标轴
plt.xlabel("文章时长")
plt.ylabel("评论数")
#设置标题
plt.title("文章时长-评论数 相关性")
#绘制散点图
plt.scatter(df_time["时长"],df_time["评论数"])

#绘制文章时长-收藏数相关性
plt.subplot(4,1,4)
#设置横纵坐标轴
plt.xlabel("文章时长")
plt.ylabel("收藏数")
#设置标题
plt.title("文章时长-收藏数 相关性")
#绘制散点图
plt.scatter(df_time["时长"],df_time["收藏数"])

#保存图片
plt.savefig(r"C:\Users\Archer\Desktop\文章时长相关性.png",bbox_inches="tight")

数据分析

发文

发文量趋势
  • 季度发文量变化趋势,可以看出存在四次明显的下降节点,“2012-04-01~2012-07-01”是因为人人都是产品经理CEO老曹大幅减少了发文数量;“2020-07-01~2020-10-01”是因为第四季度不完整;“2013-07-01~2014-04-01”“2017-10-01~2018-04-01”下降的原因未知。
  • 鉴于第一个下降节点是因为主创老曹减少了发文,蛋肥猜想后两次下降是否因为同样的原因,于是蛋肥筛选出了发文总量>90的作者,追踪其发文趋势,得到发文总量>90的作者发文量变化趋势,可以看出下降节点基本上是吻合的,同时观察所有作者发文量变化趋势,也基本吻合。
  • 推测,人人都是产品经理|产品运营版块,“2012-04-01~2013-07-01”主要是主创人员(或种子用户)发文进行版块的启动,该阶段用户还处于观望的态势;“2013-07-01~2014-04-01”主创人员(或种子用户)减少了发文,发文总量陡降,说明自然用户还未参与到版块的循环来;“2014-04-01~2017-10-01”主创人员(或种子用户)的发文量波动下降中,但整体发文量却呈现上升的趋势,追踪将所有作者的发文趋势,可以看到这段时间内发文量在0-10这个区间的作者开始增多,同时存在不少10-15这个区间的作者,说明自然用户逐渐参与到版块内容的构建中,热度引发了热情,不少专业自媒体也参与其中;“2017-10-01~2020-10-01”这段时间热情逐渐消散,版块回归理性,发文总量陡降后开始缓慢回升,且2020年由于疫情原因出现小波峰。
    Python爬虫实战(2)数据爬取、绘图、词云、分析_第2张图片

    Python爬虫实战(2)数据爬取、绘图、词云、分析_第3张图片

    Python爬虫实战(2)数据爬取、绘图、词云、分析_第4张图片
发文量排行榜
  • 江山代有才人出,各领风骚每一年哇,看得出“老虎讲运营”、“野生的独孤菌”近三年开始发力,可以关注一下,只是不知曾经叱诧风云的“魏家东”、“米可”现在又身在何方~


    Python爬虫实战(2)数据爬取、绘图、词云、分析_第5张图片

互动

  • 整体来看,用户评论数、收藏数偏低,看来大多数都是“朕已阅”的心态;作者互动平均数据,蛋肥挑选了发文量大于10的作者统计(避免单篇文章平均数据偏大),这些数据可作为一个标尺,如果你发文量大于10且三项数据能达到这个水准,那么恭喜你,你已经是人人都是产品经理的顶部KOL了。
  • 蛋肥认为可以从浏览、评论、收藏三个方面对文章(或作者)进行一个加权打分,然后就可以排出一个最具有含金量的TOP榜单,但是这个加权系数怎么设置蛋肥没有经验,所以这次先略过(还是因为懒)。
文章-浏览总数TOP10
Python爬虫实战(2)数据爬取、绘图、词云、分析_第6张图片
文章-评论总数TOP10
Python爬虫实战(2)数据爬取、绘图、词云、分析_第7张图片
文章-收藏总数TOP10
Python爬虫实战(2)数据爬取、绘图、词云、分析_第8张图片
作者-互动总数排行榜
Python爬虫实战(2)数据爬取、绘图、词云、分析_第9张图片
作者-互动平均数排行榜
Python爬虫实战(2)数据爬取、绘图、词云、分析_第10张图片

标题热词

  • 单纯从标题词云中可以看出,2012-2014年“营销”是主旋律,更多的看重如何将产品卖出去、如何提高产品的知名度,市场以抢占用户为主;从2015年开始,“运营”取而代之,很多产品的用户量接近或已经触及天花板,如何利用产品运营的技巧,尽可能大地利用这部分用户的价值成为关注的重点;2019年开始,“增长”逐步成为热议话题,无论是私域流量、社区流量,还是细分长尾市场、跨界合作等,目标都是寻求突破用户天花板,获得更多的增长空间。
    Python爬虫实战(2)数据爬取、绘图、词云、分析_第11张图片
    2012年-2020年标题热词

相关性

题目长度与浏览数、评论数、收藏数的关系
Python爬虫实战(2)数据爬取、绘图、词云、分析_第12张图片
文章时长与浏览数、评论数、收藏数的关系
Python爬虫实战(2)数据爬取、绘图、词云、分析_第13张图片

总结

  • 数据分析,最好提前设立目标,不然在选取数据、数据可视化时没有方向性,不能为了分析而分析(当然练习除外)。
  • 数据中确实隐含着很多信息,随着数据维度、时间维度、数据量等的增加,数据中所能蕴含的信息或规律也会增多,可以揭示更深层次的数据关联,这应该就是大数据的概念。
Python爬虫实战(2)数据爬取、绘图、词云、分析_第14张图片

你可能感兴趣的:(Python爬虫实战(2)数据爬取、绘图、词云、分析)