公众号后台有丰富的数据统计,但是可能依然没有我想要的统计指标。比如,我公众号粉丝量虽然不高,但是阅读率却普遍很高,那我能不能根据我公众号每篇文章的阅读率的变化情况,画一张散点图,来展现我的公众号运营成果呢?

登陆后台发现,公众号每篇文章发送情况的左侧,点击发送完毕按钮,可以看到送达人数,这是公众号发某篇文章前的粉丝数,而标题下方有阅读数。通过爬虫,依次提取每篇文章的送达人数和阅读数,根据公式:阅读率=阅读数/送达人数,就可以计算出每篇文章的阅读率了。

思路一:在进行数据可视化的时候,用该篇文章的当前粉丝数作为横轴,用该篇文章的阅读率作为纵轴,就可以画出每篇文章的阅读率分布。然后加上一条普通公众号的平均阅读率辅助线,就可以展现出本公众号的阅读率和一般公众号相比是什么水平。

思路二:还有一种思路,对文章阅读率从小到大依次进行排序,横轴为文章编号,纵轴为阅读率,这样可以画一张帕累托累进图,加上一条普通公众号平均阅读率的辅助线,就可以直观看出有多大比例的文章高于平均阅读率,并且可以让读者忽略粉丝数这条信息。

在散点图的基础上,还可以再加上文章阅读量大小,用散点的大小来表示,但是考虑到我有一百篇文章代表一百个点,有些文章的阅读率非常高,用散点大小表示的话,不便阅读,于是放弃这个思路。

预计的编程逻辑:

(1)登陆到公众号后台主页。

这一步我在第一个爬取公众号文章url链接生成pdf文档的项目中已经实现过,直接套用过来就可以。

(2)定义一个抓取送达人数和阅读数的动作。

这是个难点。

(3)进行循环,依次抓取每一页的7条文章数据,写入一个字典数据里。

公众号翻页的for循环在第一个爬取公众号文章的项目中也已经实现过了,本次稍作改编套用即可。

(4)将数据存入csv文件。

这个动作之前也实现过。

(5)通过pandas导入csv文件里的数据,并进行数据清洗,如计算阅读率。

(6)通过matplotlib等库,根据清洗好的数据,绘图。

实际实现起来,遇到了诸多问题,我们一个个解决,一步步推进。

具体步骤

导入模块

我后来导入了以下这些模块,并不是每个都用上了,并不是开始就想到要导入这些,而是在实现程序的过程中,慢慢发现需要导入某个模块。

from selenium import webdriver
import re
import time
import pickle
import csv
from selenium.common.exceptions import TimeoutException

登陆公众号后台

Python从放弃到入门那一篇,已经讲过了。构造了一个登陆的函数,之后需要调用登陆函数,传入参数为公众号的用户名和密码。

def login(username, password):#打开微信公众号登录页面driver.get('https://mp.weixin.qq.com/')driver.maximize_window()time.sleep(3)driver.find_element_by_xpath("//*[@id=\"header\"]/div[2]/div/div/div[2]/a").click()# 自动填充帐号密码driver.find_element_by_xpath("//*[@id=\"header\"]/div[2]/div/div/div[1]/form/div[1]/div[1]/div/span/input").clear()driver.find_element_by_xpath("//*[@id=\"header\"]/div[2]/div/div/div[1]/form/div[1]/div[1]/div/span/input").send_keys(username)driver.find_element_by_xpath("//*[@id=\"header\"]/div[2]/div/div/div[1]/form/div[1]/div[2]/div/span/input").clear()driver.find_element_by_xpath("//*[@id=\"header\"]/div[2]/div/div/div[1]/form/div[1]/div[2]/div/span/input").send_keys(password)time.sleep(1)#自动点击登录按钮进行登录driver.find_element_by_xpath("//*[@id=\"header\"]/div[2]/div/div/div[1]/form/div[4]/a").click()# 手动拿手机扫二维码!time.sleep(15)

定义抓取送达人数和阅读数的函数

使用Chrome浏览器登陆公众号后台,按F12查看网页代码,按ctrl+shift+C组合键来查看网页上某个具体的元素。包含“送达人数”文本的那个元素的xpath为

“//*[@id=“list”]/li[1]/div[1]/div[1]/span/div/div/div[2]/p[1]/span”。查看xpath的方式为源代码中点击这个元素所在行,右键选择-copy-copy xpath。

阅读数这个元素的xpath为“//*[@id=“list”]/li[1]/div[2]/span/div/div[2]/div/div[1]/div/span”。由于xpath是精确定位,在一个网页里某个元素只有唯一的xpath,但是我要在这个网页里提取7个同样的元素,如果我选择xpath定位,我就要查看这7个元素的构造规律。或者我可以用class等元素定位,这样我往往能找到同样的class元素出现7次,然后用for循环遍历。

几种元素定位方式我都尝试过了,在本项目中我最终决定用xpath定位的方式。读者不信邪的话可以尝试下其他定位元素的方式。

查找七个元素xpath的规律,发它们只是在li[i]中的i依次增加而已,可以用format函数进行格式化。

搜到菜鸟教程里对format函数的讲解。

格式化字符串的函数 str.format(),它增强了字符串格式化的功能。
基本语法是通过 {} 和 : 来代替以前的 % 。
format 函数可以接受不限个参数,位置可以不按顺序。

>>>"{} {}".format("hello", "world")    # 不设置指定位置,按默认顺序
'hello world'
>>> "{0} {1}".format("hello", "world")  # 设置指定位置
'hello world'
>>> "{1} {0} {1}".format("hello", "world")  # 设置指定位置
'world hello world'

于是我用format函数来构造xpath路径。

'readnum': driver.find_element_by_xpath('//*[@id=\"list\"]/li[{0}]/div[2]/span/div/div[2]/div/div[1]/div/span'.format(i)).text,

for循环构造好后,运行程序,发现提取到的数据没有送达人数,有阅读数。猜想是送达人数的数据被隐藏了,需要点击送达人数按钮,才能调用数据。
于是在每次循环的开始,都设置点击送达人数处。结果是第一行数据的送达人数有数据了,但是之后的六行都没有数据。

于是发现点击送达人数按钮后,生成的新数据框正好挡住了第二行数据,导致提取不到之后的数据。

于是设置在每一次提取完数据后,鼠标点击页面的某个位置,并且这个位置点击后可以无反应。

运行程序后,发现可以爬取数据了,但有些数据爬取不到,查看数据发现,每当有删文章的时候,删文后的下一篇文章的数据就提取不到。于是设置当程序执行失败时,也让鼠标点击页面某个无反应的位置,然后continue继续程序的循环。

运行后,发现100条数据里,有两条数据没有提取到,再次运行程序,发现又是有两条数据没有提取到,并且和上一次的两条数据不完全一样。猜想是因为程序执行过快,服务器没来得及返回数据。于是设置了每次循环睡眠1秒钟。

最终获取每一页的送达人数和阅读数的代码如下:

def get_postnum_readnum(html):lst = []for i in range(1, 8):try:driver.find_element_by_xpath("//*[@id=\"list\"]/li[{0}]/div[1]/div[1]".format(i)).click()time.sleep(1)temp_dict = {'postnum': driver.find_element_by_xpath("//*[@id=\"list\"]/li[{0}]/div[1]/div[1]/span/div/div/div[2]/p[1]/span".format(i)).text,'readnum': driver.find_element_by_xpath('//*[@id=\"list\"]/li[{0}]/div[2]/span/div/div[2]/div/div[1]/div/span'.format(i)).text,'title': driver.find_element_by_xpath('//*[@id="list"]/li[{0}]/div[2]/span/div/div[2]/a/span'.format(i)).get_attribute('textContent'),'date': driver.find_element_by_xpath("//*[@id=\"list\"]/li[{0}]/div[1]/em".format(i)).text,}driver.find_element_by_xpath("//*[@id=\"list_container\"]/div[1]/div[2]/div/span/input").click()lst.append(temp_dict)except:driver.find_element_by_xpath("//*[@id=\"list_container\"]/div[1]/div[2]/div/span/input").click()continuereturn lst

进行循环,依次抓取每页的7条数据

代码和Python从放弃到入门那一篇差不多。

#用webdriver启动谷歌浏览器
chrome_driver = r"C:\Users\jiansi\PycharmProjects\jiansidata\venv\Lib\site-packages\selenium\webdriver\chrome\chromedriver.exe"
driver = webdriver.Chrome(executable_path=chrome_driver)
"""需要手动输入个人微信公众号的账号,密码,要导出的公众号名称"""
username = '' # 账号
password = '' # 密码
login(username, password)
page_num = int(driver.find_elements_by_class_name('weui-desktop-pagination__num__wrp')[-1].text.split('/')[-1])
# 点击下一页
num_lst = get_postnum_readnum(driver.page_source)
#print(num_lst)
for _ in range(1, page_num):try:pagination = driver.find_elements_by_class_name('weui-desktop-pagination__nav')[-1]pagination.find_elements_by_tag_name('a')[-1].click()time.sleep(5)num_lst += get_postnum_readnum(driver.page_source)except:continue

将数据存入csv文件

代码和Python从放弃到入门那一篇差不多。

with open('2.csv', 'w', encoding="utf-8", newline='') as f:writer = csv.DictWriter(f, fieldnames=['postnum', 'readnum', 'title', 'date'])writer.writeheader()writer.writerows(num_lst)

通过pandas导入csv数据,并进行数据清洗

从这一步开始,我新建了一个文件写入。

导入模块,不一定全用上了。

import sys
import pandas as pd
import csv
import matplotlib.pyplot as plt
from matplotlib.pyplot import savefig
import matplotlib as mpl
import numpy as np
import seaborn as sns
from datetime import datetime
from pandas import to_datetime

我先读取csv表格里的数据,看看读取效果。

"""用pandas读取csv文件里的数据,生成二维表,并合并两张表"""
df1 = pd.read_csv('1.csv', delimiter=',', sep='\t', encoding='utf-8')
df2 = pd.read_csv('2.csv', delimiter=',', sep='\t', encoding='utf-8')
print(df1)
print(df2)
df1.info()
df2.info()

可能会报错,原因和encoding的编码格式有关,可是尝试改变编码格式,从gbk换为gbk18030,或者再换位utf-8,unicode等。
df.info()是查看数据的基本情况,方便观察数据有没有空值等错误。这次数据没有空值,所以处理空值等错误的操作这里就没有采用。

因为我提取了两个公众号的数据,要将两个公众号的数据合并,并且我只需要csv数据里的某几列。

cols1 = df1[['postnum', 'readnum', 'title', 'date']]
cols2 = df2[['postnum', 'readnum', 'title', 'date']]
df3 = cols1.append(cols2, ignore_index=True)
print(df3)

df3就是我合并两张表之后的数据。
由于我的送达人数这列的数据不是纯数字,而是**人的字符串,我需要去掉这个人字,并且变为整数型数据。

我找了一些pandas教程或者公式集锦,发现都没有较如何对某一列的数据进行处理。

后来才知道用pandas里的apply()函数可以实现。并且,apply函数还可以实现对某些列进行运算生成新的列,所以计算阅读率的任务也可以通过apply()函数完成了。实际上Excel里面使用函数的各种操作,在pandas里面基本就可以用apply()函数完成了。

链接这篇文章对apply(),map(),applymap()函数的讲解就很不错。https://zhuanlan.zhihu.com/p/100064394?utm_source=wechat_session

我实现去掉数据里“人”字的代码

"""实现更改postnum列的149人这类数据为149,更改刷新到dataframe中。"""
def postnum_int(series):postnum = series['postnum']postnum_int = int(postnum[0:-1])return postnum_int
df3['postnum'] = df3.apply(postnum_int, axis=1)
print(df3)

在这串代码中,我通过定义一个变换方法,然后用apply函数引用这种变换方法,按列刷新,把生成的数据改到原来那一列。
类似地,我生成了阅读率数据。

"""增加阅读率数据"""
def read_rate(series):postnum = series['postnum']readnum = series['readnum']read_rate = readnum / postnumreturn read_rate
df3['read_rate'] = df3.apply(read_rate, axis=1)
print(df3)

排序方式可以用sort_index()函数按序号排序,也可以用sort_values()函数按值排序。
我用两种排序方式生成了两个数据。

"""对dataframe按照postnum从小到大进行排序"""
df4 = df3.sort_values(axis=0, ascending=True, by='postnum')
print(df4)
"""对dataframe按照read_rate从小到大进行排序"""
df5 = df3.sort_values(axis=0, ascending=True, by='read_rate')
print(df5)

将数据传入matplotlib的绘图函数后发现,有的阅读率太高了,影响图的效果,于是决定删掉几个阅读率太高的数据,剔除掉三个阅读率高于1500%的数据。
用drop()函数进行删除某一行数据的操作。

"""删除某一行数据"""
df6 = df4.drop(df4[df4.read_rate > 15].index, inplace=False)
print(df6)
df7 = df5.drop(df5[df5.read_rate > 15].index, inplace=False)
print(df7)

matplotlib绘图

保存了一张png图片到文件夹。我在图中加了两条辅助线,一条红线代表阅读率8%,一条绿线代表阅读率50%。

"""使用matplotlib生成气泡图,按照postnum排序"""
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(df6['postnum'], df6['read_rate'], )
ax.set_xlabel('postnum')
ax.set_ylabel('read_rate')
plt.axhline(y=0.08, ls=":", c="red")
plt.axhline(y=0.5, ls=":", c="green")
plt.savefig('readrate1.png', dpi=750, bboxinches='tight')
plt.show()

从图中可见,阅读率普遍高于8%,也普遍高于50%。

按照思路二,将文章按阅读率从小到大排序,横轴为文章序号,纵轴为阅读率,更直观展现高于某一阅读率的文章比例。

从图中可见,我有约80%的文章阅读率超过50%,有超过95%的文章阅读率超过8%,有约20%的文章阅读率超过400%。以下为这张图的代码实现。

df7['index'] = np.arange(len(df7))

df7为按阅读率排序后的数组,上面这一句的目的是生成一列index,按照每一条数据的行号输出编号。

"""用matplotlib生成散点图,横轴为文章序号"""
df7['index'] = np.arange(len(df7))
print(df7)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(df7['index'], df7['read_rate'], )
ax.set_xlabel('index')
ax.set_ylabel('read_rate')
plt.axhline(y=0.08, ls=":", c="red")
plt.axhline(y=0.5, ls=":", c="green")
plt.savefig('readrate1.png', dpi=750, bboxinches='tight')
plt.show()

以上这个项目就完成了。

画图的几个包有matplotlib、seaborn、plotnine,还有pyecharts,有兴趣的可以体验下其他几个绘图包。

Python进阶之路(3):提取公众号后台数据并数据可视化相关推荐

  1. python 报价机器人_100行代码实现报价机器人公众号后台

    前言 公众号后台使用Python Flask进行快速开发,核心代码不到100行.因此,开源这个应用的目的也只是为了将其作为一个微信公众号自动回复的HelloWorld项目,给感兴趣的同学提供一点参考. ...

  2. Python 进阶之路 (九) 再立Flag, 社区最全的itertools深度解析(上)

    前言 大家好,今天想和大家分享一下我的itertools学习体验及心得,itertools是一个Python的自带库,内含多种非常实用的方法,我简单学习了一下,发现可以大大提升工作效率,在sf社区内没 ...

  3. Python 进阶之路 (十二) 尾声即是开始

    Python进阶之路总结 大家好,我的<< Python进阶之路>>到这一期就到此为止了,和 <<Python 基础起步>>不同,在掌握了一些基础知识后 ...

  4. python交互式程序设计导论第二周_沧州学堂云Python 交互式程序设计导论搜题公众号...

    关注公众号[帅搜]即可免费查询 网站:xuanxiu365(网址速记:选修365) 支持:大学网课,智慧树,知到,超星,,,选修课,,外语类,财会类,建筑类,职业资格,考试,类,外贸类,计算机类等考试 ...

  5. Python 进阶之路 (八) 最用心的推导式详解 (附简单实战及源码)

    什么是推导式 大家好,今天为大家带来问我最喜欢的Python推导式使用指南,让我们先来看看定义~ 推导式(comprehensions)是Python的一种独有特性,推导式是可以从一个数据序列构建另一 ...

  6. [python]用flask框架搭建微信公众号的后台

    用flask框架搭建微信公众号的后台 最近用python写了点爬虫,为了要让爬取的数据能够随时显示在我眼前,并实时根据我的指令返回数据.于是采用微信公众号做这个显示窗口,既能发送指令也能显示简单的相关 ...

  7. 腾讯云+CentOS 7.2+python:搭建微信公众号后台入门教程

    因为想自己编写微信公众号后台的程序,实现一些小功能,为以后的开发做准备,所以就从零开始,摸索着注册微信公众号,租用服务器,搭建环境进行开发. 简单的搭建过程中也经历了一些波折,踩了不少坑,仅留此文给跟 ...

  8. 如何提取公众号文章中的音频

    温馨提示:建议收藏本文章,工具地址有更新时方便第一时间知晓. 如何提取公众号文章中的很好听的小哥哥或者小姐姐的音频,这是困扰很多公众号运营者的问题,前几天看到一个很好的工具,可以很方便的提取公众号文章 ...

  9. 4ye含泪用python爬取了自己的公众号粉丝数据

    4ye含泪用python爬取了自己的公众号粉丝数据 小伙伴们好呀,最近本来是在捣鼓Gateway的知识点的,结果被一件事情搞得心不在焉 哈哈哈哈,结果不得不先鸽下~ 搞完这件事情再继续哦!! ε=ε= ...

  10. python公众号教学_Python微信公众号后台开发教程

    本篇文章开启Python开发微信公众号后台 准备: 云服务平台:(我用的是京东云的) 公众号:自己注册一个就行 Git:代码管理平台 了解平台 进入公众号后台,查看开发相关项 基本配置(服务器配置等) ...

最新文章

  1. 设计模式之外观模式php,php设计模式(十五)外观模式
  2. “好像在哪见过你”现在有了科学解释,一群脑细胞帮你回忆那张脸 | Science
  3. linux vi 打开乱码,liunx 中使用vim 打开 txt文件时 中文出现乱码的解决办法
  4. 小米速度!雷军再祭 All in AIoT 大招!
  5. 数据库系统原理及mysql应用教程第二版_数据库系统原理及MySQL应用教程(第2版十三五普通高等教育规划教材)...
  6. win10 没有计算机策略,Win10家庭版没有组策略怎么办
  7. 天线的主要技术指标及其含义—天线的输入阻抗
  8. Revisiting Graph Contrastive Learning from the Perspective of Graph Spectrum
  9. 视频文件格式、视频封装格式、视频编码方式
  10. 传统手绘建模与次世代建模技术与流程的区别,在未来游戏行业中作为建模人如何做出职业规划
  11. D3D11 加载静态3D模型(.obj格式)
  12. Hi3559AV100 RTSP推H265视频码流使用VLC软件本地直播
  13. 为什么剩余数不能相加_为什么花的钱剩下的相加不等于花去的数
  14. 工业机器人九龙坡区职教中心_重庆市九龙坡职业教育中心(职教中心)简介简介...
  15. 如何快速打造淘宝爆款
  16. Linux内核之32/64位除法
  17. 正宇丨揭秘你不知道的网络水军产业链运作内幕
  18. wine linux 64位下载,Wine 1.9 开发者版本可以下载了
  19. sip客户端源码c语言,SIP客户端选型
  20. 物联网大学生就业培训有哪些?物联网工程师培训机构靠谱吗?

热门文章

  1. 读取QQ ClientKey C++版本
  2. gsp计算机系统系统操作培训,gsp计算机系统操作
  3. 海思Hi3519A SVP从入门到精通(一 概述)
  4. 阅读mybatis源码
  5. duet连win10_duetdisplay这个软件在win10上用不了?安装vs2015的时候想取消安装没有点取消...
  6. Java Poi向下填充公式
  7. VDN互联网数据驱动 FOR PB
  8. 【AI番外】微信小程序前后端开发
  9. 最全Pycharm教程(5)——Python快捷键相关设置
  10. 【论文复现】ReLU、Leaky ReLU、PReLU、RReLU实验对比(2015)