2022年马上要结束了,最近突然有个想法,利用微信小程序+博客园接口做了一个「博客园年度总结」,统计下自己写博客这些年的数据情况,最终效果如下
在开始之前先捋一捋思路,大致要实现以下几个功能:
1、使用flask提供后端服务,调用博客园接口获取数据,然后作进一步处理,给微信小程序提供接口;
2、在小程序中通过上划/下划来切换页面;
3、在小程序中展示数据图表(接入echarts);
4、在最后一页能够通过点击按钮进行回看和分享;
本章来写一下后端处理逻辑
本次需要调用博客园如下接口来获取相关数据:
1、获取当前登录用户信息,接口文档:https://api.cnblogs.com/help#4e598eb53dda7bd5ed0291edd7155871
这个接口返回你的「博客名」、「园龄」、「粉丝数」等,如下
{
"UserId": "xxx",
"SpaceUserID": xxx,
"BlogId": xxx,
"DisplayName": "我是冰霜",
"Face": "https://pic.cnblogs.com/face/1158674/20201224122945.png",
"Avatar": "https://pic.cnblogs.com/avatar/1158674/20201224122945.png",
"Seniority": "5年5个月",
"BlogApp": "hanmk",
"FollowingCount": 16,
"FollowerCount": 532
}
2、获取个人博客信息,接口文档:https://api.cnblogs.com/help#f8c559cde161e9a9d08c9e84cb106cc3
这个接口主要返回你的「博客数量」、「博客签名」等
{
"blogId": xxx,
"title": "我是冰霜",
"subtitle": "I am just a sunflower, waiting for my only sunshine.",
"postCount": 245,
"pageSize": 10,
"enableScript": true
}
3、获取个人博客随笔列表,接口文档:https://api.cnblogs.com/help#691c5586990ad52adc500024fc6f260b
这个接口返回你的博客随笔列表
[
{
"Id": 1,
"Title": "sample string 2",
"Url": "sample string 3",
"Description": "sample string 4",
"Author": "sample string 5",
"BlogApp": "sample string 6",
"Avatar": "sample string 7",
"PostDate": "2017-06-25T20:15:30.2514989+08:00",
"ViewCount": 9,
"CommentCount": 10,
"DiggCount": 11
},
{
"Id": 1,
"Title": "sample string 2",
"Url": "sample string 3",
"Description": "sample string 4",
"Author": "sample string 5",
"BlogApp": "sample string 6",
"Avatar": "sample string 7",
"PostDate": "2017-06-25T20:15:30.2514989+08:00",
"ViewCount": 9,
"CommentCount": 10,
"DiggCount": 11
}
]
前2个接口比较简单,数据拿来后可以直接返给前端去用,但是最后一个「获取随笔列表」接口,从博客园拿到数据后还需要加工一下,达到如下目的
- 把发布的第一篇博客单独拎出来;
- 提取浏览量排名前6的随笔;
- 统计每年创建的随笔总数;
- 统计一年当中每个月创建的随笔总数;
重点代码实现逻辑
1、获取当前登录用户信息
def get_users(self):
"""获取当前登录用户信息接口"""
url = "https://api.cnblogs.com/api/users"
try:
res = requests.get(url=url, headers=self.headers)
data = res.json()
# print(res.request.headers)
# print(data)
return data
except Exception as e:
raise e
2、获取个人博客信息
def get_blog_info(self, blog_name):
"""获取个人博客信息接口"""
url = "https://api.cnblogs.com/api/blogs/" + blog_name
try:
res = requests.get(url=url, headers=self.headers)
data = res.json()
# print(data)
return data
except Exception as e:
raise e
3、获取个人博客随笔列表
def deal_blogs(blogs):
"""处理从博客园获取到的随笔数据"""
new_data = None
if blogs:
new_data = {
"Title": blogs["Title"], # 标题
"PostDate": blogs["PostDate"].split("T")[0], # 发布时间(截取日期部分,如2022-09-07)
"ViewCount": blogs["ViewCount"], # 浏览次数
"CommentCount": blogs["CommentCount"], # 评论次数
"DiggCount": blogs["DiggCount"] # 点击次数
}
return new_data
def get_blogs_api(self, blog_name):
"""获取个人随笔列表接口"""
flag = True
try:
blogs = []
i = 1
while flag is True:
url = "https://api.cnblogs.com/api/blogs/{}/posts?pageIndex={}".format(blog_name, i)
res = requests.get(url=url, headers=self.headers)
data = res.json()
# print(data)
# print(i)
if data:
blogs += data
i += 1
else:
# print(data)
flag = False
# print(len(blogs))
# print(blogs)
# print("123")
new_blogs = list(map(self.deal_blogs, blogs))
# [{'Title': 'xx', 'PostDate': 'xx-xx-xx', 'ViewCount': xx, 'CommentCount': 0, 'DiggCount': 0}, {}, {}]
# print(new_blogs)
first_blog = new_blogs[-1] # 发布的第一篇博客
# {'Title': 'xx', 'PostDate': 'xx-xx-xx', 'ViewCount': xx, 'CommentCount': 0, 'DiggCount': 0}
# print(first_blog)
sort_blogs = sorted(new_blogs, key=lambda item: item["ViewCount"], reverse=True) # 按照ViewCount排序,降序
print(sort_blogs)
view_max_10 = sort_blogs[0:10] # 浏览量前10的文章
# print(view_max_10)
"""提取2022年的月度数据并处理"""
blog_date1 = [i["PostDate"][0:7] for i in new_blogs] # 提取每条数据的年月,组成一个列表
# print(blog_date1)
temp = Counter(blog_date1)
month_blog_date = dict(temp)
# print(month_blog_date)
months = ["2022-01", "2022-02", "2022-03", "2022-04", "2022-05", "2022-06",
"2022-07", "2022-08", "2022-09", "2022-10", "2022-11", "2022-12"]
month_result = [] # 2022年每月博客新增数量
for j in months: # 遍历日期范围列表
if j in month_blog_date:
# 如果一个日期在bug列表中,说明这个日期有值,取bug字典中该日期的值赋给bug_num,同时date取当前日期,组合为一个字典
month_result.append({"date": j, "value": month_blog_date[j]})
else:
# 否则这个日期对应的value=0
month_result.append({"date": j, "value": 0})
# print(month_result)
now_year_blog_sum = sum([i["value"] for i in month_result]) # 2022年新增博客总数
# print(now_year_blog_sum)
"""提取年度数据并处理"""
blog_date2 = [i["PostDate"][0:4] for i in new_blogs] # 提取每条数据的年,组成一个列表
year_blog_date = dict(Counter(blog_date2))
# print(year_blog_date)
begin_year = first_blog["PostDate"][0:4]
# print(begin_year)
end_year = get_now_year()
# print(end_year)
# print(type(begin_year), type(end_year))
date_gap = int(end_year) - int(begin_year) + 1
years = []
for i in range(date_gap):
years.append(str(int(begin_year) + i))
# print(years)
year_result = [] # 每年博客新增数量
for j in years: # 遍历日期范围列表
if j in year_blog_date:
# 如果一个日期在bug列表中,说明这个日期有值,取bug字典中该日期的值赋给bug_num,同时date取当前日期,组合为一个字典
year_result.append({"date": j, "value": year_blog_date[j]})
else:
# 否则这个日期对应的value=0
year_result.append({"date": j, "value": 0})
# print(year_result)
res = {
"first_blog": first_blog, # 发布的第一篇博客
"view_max_10": view_max_10, # 浏览量前10的文章
"now_year_blog_sum": now_year_blog_sum, # 2022年新增博客总数
"month_result": month_result, # 2022年每月博客新增数量
"year_result": year_result # 每年博客新增数量
}
print(res)
return res
except Exception as e:
raise e
代码说明:
1、deal_blogs()
函数
我打算使用python的map函数来处理原始数据,所以这里先定义一个数据处理函数,
从博客园接口获取到的数据格式如下,一个列表包含多个字典
[
{
"Id": 1,
"Title": "sample string 2",
"Url": "sample string 3",
"Description": "sample string 4",
"Author": "sample string 5",
"BlogApp": "sample string 6",
"Avatar": "sample string 7",
"PostDate": "2017-06-25T20:15:30.2514989+08:00",
"ViewCount": 9,
"CommentCount": 10,
"DiggCount": 11
},
...
...
...
]
每个字典中有很多字段,我只想提取其中一些必要的字段,只保留Title
、PostDate
、ViewCount
等字段
def deal_blogs(blogs):
"""处理从博客园获取到的随笔数据"""
new_data = None
if blogs:
new_data = {
"Title": blogs["Title"], # 标题
"PostDate": blogs["PostDate"].split("T")[0], # 发布时间(截取日期部分,如2022-09-07)
"ViewCount": blogs["ViewCount"], # 浏览次数
"CommentCount": blogs["CommentCount"], # 评论次数
"DiggCount": blogs["DiggCount"] # 点击次数
}
return new_data
2、get_blogs_api()
函数
为了方便,我把数据处理过程都写到这个函数中了,然后统一返回出去
(1)循环分页调用获取随笔列表接口
在调用博客园随笔列表接口时,需要传入pageIndex
因为我们并不知道一共有多少页数据,所以这里我使用了while
循环,当接口返回空时说明到了最后一页
... ...
... ...
flag = True
try:
blogs = []
i = 1
while flag is True:
url = "https://api.cnblogs.com/api/blogs/{}/posts?pageIndex={}".format(blog_name, i)
res = requests.get(url=url, headers=self.headers)
data = res.json()
# print(data)
# print(i)
if data:
# 如果接口有返回数据,就把数据追加到blogs中,同时页码+1
blogs += data
i += 1
else:
# 如果接口返回空,说明当前传入的页码已经没有没有数据了,结束循环
# print(data)
flag = False
new_blogs = list(map(self.deal_blogs, blogs)) # 调用map函数处理博客原始数据
... ...
... ...
(2)提取发布的的第一篇博客
因为博客园随笔列表接口返回的数据默认是按照倒序排列的,所以最后一条就是发布的第一篇博客
first_blog = new_blogs[-1] # 发布的第一篇博客
(3)获取浏览量为前10的博客
需要对数据按照「浏览量」进行排序,然后取前10条即可
可以通过sorted()
函数来实现
sort_blogs = sorted(new_blogs, key=lambda item: item["ViewCount"], reverse=True) # 按照ViewCount排序,降序
print(sort_blogs)
view_max_10 = sort_blogs[0:10] # 浏览量前10的文章
打印结果
[{'Title': '如何查看linux服务器内存使用情况', 'PostDate': '2019-03-19', 'ViewCount': 200087, 'CommentCount': 4, 'DiggCount': 0}, {'Title': 'python+selenium基础之XPATH定位(第一篇)', 'PostDate': '2018-05-06', 'ViewCount': 109768, 'CommentCount': 9, 'DiggCount': 0}, {'Title': 'oracle导入dmp文件的2种方法', 'PostDate': '2017-07-26', 'ViewCount': 102784, 'CommentCount': 1, 'DiggCount': 0}, {'Title': 'python爬虫学习(一):BeautifulSoup库基础及一般元素提取方法', 'PostDate': '2018-04-05', 'ViewCount': 100563, 'CommentCount': 9, 'DiggCount': 0}, {'Title': 'python读取配置文件&&简单封装', 'PostDate': '2018-10-24', 'ViewCount': 87415, 'CommentCount': 0, 'DiggCount': 0}, {'Title': 'oracle导出dmp文件的2种方法', 'PostDate': '2017-07-26', 'ViewCount': 75031, 'CommentCount': 0, 'DiggCount': 0}, {'Title': '利用拷贝data目录文件的方式迁移mysql数据库', 'PostDate': '2017-12-05', 'ViewCount': 64000, 'CommentCount': 3, 'DiggCount': 0}, {'Title': 'linux下查看进程id时用到的命令', 'PostDate': '2019-03-18', 'ViewCount': 63239, 'CommentCount': 0, 'DiggCount': 0}, {'Title': '在python中使用正则表达式(一)', 'PostDate': '2018-06-06', 'ViewCount': 58195, 'CommentCount': 2, 'DiggCount': 0}, {'Title': 'postman(十一):添加cookie', 'PostDate': '2019-04-29', 'ViewCount': 54866, 'CommentCount': 0, 'DiggCount': 0}, {'Title': '使用Dockerfile创建一个tomcat镜像,并运行一个简单war包', 'PostDate': '2018-03-10', 'ViewCount': 39556, 'CommentCount': 2, 'DiggCount': 0}, {'Title': 'postman(六):详解在Pre-request Script中如何执行请求', 'PostDate': '2018-12-30', 'ViewCount': 37908, 'CommentCount': 11, 'DiggCount': 0}, {'Title': '如何在jenkins上新建一个项目及其简单配置', 'PostDate': '2017-05-04', 'ViewCount': 28941, 'CommentCount': 0, 'DiggCount': 0}, {'Title': 'python日志模块的使用', 'PostDate': '2019-02-28', 'ViewCount': 28605, 'CommentCount': 1, 'DiggCount': 0}, {'Title': 'nginx反向代理实例', 'PostDate': '2018-07-10', 'ViewCount': 27529, 'CommentCount': 1, 'DiggCount': 0}, {'Title': 'SQL查询--索引', 'PostDate': '2019-08-31', 'ViewCount': 26935, 'CommentCount': 0, 'DiggCount': 0}, {'Title': 'postman(二):使用postman发送get or post请求', 'PostDate': '2018-12-20', 'ViewCount': 24487, 'CommentCount': 0, 'DiggCount': 0}, {'Title': 'python基础:删除列表中特定元素的几种方法', 'PostDate': '2020-10-11', 'ViewCount': 23846, 'CommentCount': 0, 'DiggCount': 0}, {'Title': '使用pymysql操作数据库', 'PostDate': '2018-06-22', 'ViewCount': 23806, 'CommentCount': 0, 'DiggCount': 0}, {'Title': 'python之做一个简易的翻译器(一)', 'PostDate': '2019-04-14', 'ViewCount': 17938, 'CommentCount': 1, 'DiggCount': 0}, {'Title': 'python爬虫学习(三):使用re库爬取"淘宝商品",并把结果写进txt文件', 'PostDate': '2018-04-08', 'ViewCount': 17260, 'CommentCount': 2, 'DiggCount': 0}, {'Title': '在不安装oracle客户端的情况下,使用PLSQL', 'PostDate': '2018-11-27', 'ViewCount': 15983, 'CommentCount': 2, 'DiggCount': 0}, {'Title': '使用“rz -be”命令上传文件至服务器;使用“sz 文件名”从服务器下载文件到本地', 'PostDate': '2018-07-02', 'ViewCount': 15654, 'CommentCount': 0, 'DiggCount': 0}, {'Title': 'python多线程:控制线程数量', 'PostDate': '2020-05-30', 'ViewCount': 15577, 'CommentCount': 0, 'DiggCount': 0}, {'Title': '理解css相邻兄弟选择器', 'PostDate': '2018-05-19', 'ViewCount': 15303, 'CommentCount': 3, 'DiggCount': 0},... ...]
(4)本次我要做2张柱状图图表:2022年月度新增随笔趋势、2017~2022年度新增随笔趋势
因为之前有提取jira数据做质量看板的经验,这次处理起来就驾轻就熟了(传送门:基于jira数据开发一个质量看板)
下面是月度数据和年度数据的处理逻辑
"""提取2022年的月度数据并处理"""
blog_date1 = [i["PostDate"][0:7] for i in new_blogs] # 提取每条数据的年月,组成一个列表
# print(blog_date1)
temp = Counter(blog_date1)
month_blog_date = dict(temp)
# print(month_blog_date)
months = ["2022-01", "2022-02", "2022-03", "2022-04", "2022-05", "2022-06",
"2022-07", "2022-08", "2022-09", "2022-10", "2022-11", "2022-12"]
month_result = [] # 2022年每月博客新增数量
for j in months: # 遍历日期范围列表
if j in month_blog_date:
# 如果一个日期在bug列表中,说明这个日期有值,取bug字典中该日期的值赋给bug_num,同时date取当前日期,组合为一个字典
month_result.append({"date": j, "value": month_blog_date[j]})
else:
# 否则这个日期对应的value=0
month_result.append({"date": j, "value": 0})
# print(month_result)
now_year_blog_sum = sum([i["value"] for i in month_result]) # 2022年新增博客总数
"""提取年度数据并处理"""
blog_date2 = [i["PostDate"][0:4] for i in new_blogs] # 提取每条数据的年,组成一个列表
year_blog_date = dict(Counter(blog_date2))
# print(year_blog_date)
begin_year = first_blog["PostDate"][0:4] # 取发布的第一篇博客所在的年份,因为这就是博客起始年份
# print(begin_year)
end_year = get_now_year() # 取当年年份为结束年份
# print(end_year)
# print(type(begin_year), type(end_year))
date_gap = int(end_year) - int(begin_year) + 1 # 计算年份差
years = [] # 定义年份范围
for i in range(date_gap):
years.append(str(int(begin_year) + i))
# print(years)
year_result = [] # 每年博客新增数量
for j in years: # 遍历年份范围列表
if j in year_blog_date:
# 如果一个日期在bug列表中,说明这个日期有值,取bug字典中该日期的值赋给bug_num,同时date取当前日期,组合为一个字典
year_result.append({"date": j, "value": year_blog_date[j]})
else:
# 否则这个日期对应的value=0
year_result.append({"date": j, "value": 0})
# print(year_result)
最后把这些数据放到一个字典中返回出去即可
res = {
"first_blog": first_blog, # 发布的第一篇博客
"view_max_10": view_max_10, # 浏览量前10的文章
"now_year_blog_sum": now_year_blog_sum, # 2022年新增博客总数
"month_result": month_result, # 2022年每月博客新增数量
"year_result": year_result # 每年博客新增数量
}