前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Fluid -24- Leancloud 失效解决方案 —— 自建站点 PV UV 统计

Fluid -24- Leancloud 失效解决方案 —— 自建站点 PV UV 统计

作者头像
为为为什么
发布2022-09-22 15:30:49
9790
发布2022-09-22 15:30:49
举报
文章被收录于专栏:又见苍岚

2022年8月,LeanCloud 国际版不再为来自中国大陆的 IP 提供服务,基于 LeanCloud 的站点统计因此失效,本文基于 Umami 的统计信息自建 PV UV 统计后台,解决上述问题。 前端、后端小白,对Python比较熟悉,后端代码用 Python 实现的,仅仅完成了功能,过程也比较繁琐,思路过程供大家参考

背景

  • 基于某些原因,LeanCloud 国际版不再为来自中国大陆的 IP 提供服务
  • 在 Hexo Fluid 主题中使用 LeanCloud 的主要有 站点/文章 PV、UV 统计和评论系统
  • 截止当前(2022年8月15日)Walline 的 LeanCloud 数据库可以正常访问,即仍在正常运转,可能是有后台的代理服务器
  • 站点 PV、UV 凉了,于是自建

功能需求

  • 全站页面浏览量 (PV) 统计
  • 全站用户访问量 (UV) 统计
  • 当前在线用户数统计
  • 文章页面浏览量统计
  • 文章用户访问量统计

原理思路

计数工具
  • 讲道理只要有看门的 callback 将用户信息发送到后台进行统计并想办法显示统计数据即可
  • github 上有很多工程可以使用
  • 我在之前搭建了基于 Google 统计的工具 Umami
  • 正好 Umami 有方便的 API 接口 可以调用

于是决定基于这款工具开发 PV UV 统计

LeanCloud 数据继承
  • 如果直接放弃 LeanCloud 那么之前的访问数据就清零了
  • 要是觉得可惜的话可以将 LeanCloud 数据下载下来,在 Umami 计数结果中加上 LeanCloud 的以往数据即可 当然了,优秀的同学也可以去改 Umami 的数据库
后台代码
  • 需要后端服务器监听端口,负责为显示 PVUV 数据请求提供服务
  • 核心就是对 Umami API 的封装
  • 需要解决跨域访问的问题
前端显示
  • 建立几个 span 块,JavaScript 代码动态修改并填入数据内容
https 访问
  • https 站点 要求内部链接都是 https
  • 于是需要 Nginx 反向代理成 https
插入 Fluid
  • 在Fluid 合适的部分插入上述前端代码,完成在 Hexo 中的显示

实现流程

计数工具
LeanCloud 数据继承
  • 如果有之前的 LeanCloud 国际版数据可以导出为 Json
  • 想办法(大陆IP无法访问)进入 国际版 LeanCloud
  • 导出数据
  • 导出成功
  • 随后可以看到数据库表单基本整理成了json文件下载了下来
  • 对于本文应用来说,核心文件在 Counter.0.jsonl 文件中,该文件主要内容为 json 格式,删去第一行稍加修改即可作为正常 json 文件使用
  • 之后可以按照自己的需求整理成方便可用的计数文件
后台代码

依赖 Umami 的 API,需要搭建好 API 获取环境

核心代码
代码语言:javascript
复制
import requests
import json
import mtutils as mt
from pathlib import Path


class Statistic:
    root_url = "http://<url-to-umami>/api/website/1/"
    header={
        "Accept": "application/json",
        "Access-Control-Allow-Origin": '*',
        "Authorization": "Bearer <your-token>"
    }

    def __init__(self):
        conter_path = Path(__file__).with_name('conter.json')
        self.conter_dict = mt.json_load(conter_path)

    @staticmethod
    def make_json_str(pv, uv, act):
        res_str = "(function vvdstatistics(){"+\
                "var PVstatic='" +\
                str(pv) +\
                "';var dom=document.querySelector('#PVstatic');Array.isArray(dom)?dom[0].innerText=PVstatic:dom.innerText=PVstatic;"+\
                "var UVstatic='" +\
                str(uv) +\
                "';var dom=document.querySelector('#UVstatic');Array.isArray(dom)?dom[0].innerText=UVstatic:dom.innerText=UVstatic;"+\
                "var ACTstatic='" +\
                str(act) +\
                "';var dom=document.querySelector('#ACTstatic');Array.isArray(dom)?dom[0].innerText=ACTstatic:dom.innerText=ACTstatic;"+\
                "})()"

        return res_str

    def active_num(self):
        url = self.root_url + 'active'
        res = requests.get(url=url, data=json.dumps({}), headers=self.header)
        res_dict = json.loads(res.text)
        act_str = max(1, res_dict[0]['x'])
        return act_str

    def PVUV_num(self):
        url = self.root_url + 'stats?start_at=1350679719687&end_at=1990039038644'
        res = requests.get(url=url, data=json.dumps({}), headers=self.header)
        res_dict = json.loads(res.text)
        pv = res_dict['pageviews']['value'] + self.conter_dict['site-pv']
        uv = res_dict['uniques']['value'] + self.conter_dict['site-uv']
        return pv, uv

    def js_str(self):
        pv, uv = self.PVUV_num()
        act = self.active_num()
        return self.make_json_str(pv, uv, act)
    
    def post_pv(self, sub_url):
        url = self.root_url + 'stats?start_at=1350679719687&end_at=1990039038644' + '&url=' + sub_url
        res = requests.get(url=url, data=json.dumps({}), headers=self.header)
        res_dict = json.loads(res.text)
        pv = res_dict['pageviews']['value'] + self.conter_dict.get(sub_url, 0)
        uv = res_dict['uniques']['value']
        return pv, uv

  • 使用时需要修改 root_urlheader 中的 <url-to-umami><your-token> 为你自己的值
  • active_num 函数获取当前活跃用户数
  • PVUV_num 函数获取站点 PV UV 数
  • post_pv 函数获取 post PV UV 数
  • js_str 函数整合 active_numPVUV_num 的结果返回 js 代码
  • self.conter_dict 为 LeanCloud 计数数据备份 Json 字典
  • 核心代码的行为:
代码语言:txt
复制
- 利用 Umami API 获取需要的数据
- 整合成 js 字符串或直接返回数据
- js 串功能为修改ID 为 `PVstatic`, `UVstatic` 和 `ACTstatic` 的元素内容
后端代码
代码语言:javascript
复制
from flask import Flask, request
from flask_cors import CORS
import mtutils as mt

from lib import statis_obj


port = '<your-port>'
log_file_path = '/usr/local/static/log.log'
app = Flask(__name__)
app.logger = mt.log_init(log_file_path)
CORS(app, supports_credentials=True)


@app.route("/statistics", methods=['GET','POST'])
def statistics():
    res = statis_obj.js_str()
    return res


@app.route("/poststats", methods=['GET','POST'])
def poststats():
    url = request.data.decode('utf8')[1:-1]
    pv, uv = statis_obj.post_pv(url)
    return {'pv': pv, 'uv': uv}


if __name__ == '__main__':
    app.logger("**************** Static Sever Start *******************")
    app.run('0.0.0.0', port=port)
    pass

  • 使用时将 <your-port> 换为你自己需要监听的端口
  • 开放两个路由:
代码语言:txt
复制
-  **statistics** 站点 PV UV 和 活跃用户数,返回内容为一段 js 代码  访问示例
 
代码语言:txt
复制
-  **poststats** 文章 PV UV  访问示例
 
搭建服务

代码调整好后需要让他在服务器自动运行

需要用到 systemctl 工具

此处 service 示例代码

代码语言:javascript
复制
[Unit]
Description = Service to count pv,uv for www.zywvvd.com
After = network.target

[Service]
ExecStart = /home/lighthouse/anaconda3/bin/python main.py
WorkingDirectory = /usr/local/static/
StandardOutput = inherit
StandardError = inherit
Restart = always
User = lighthouse

[Install]
WantedBy=multi-user.target

记得设置开机自动启动

代码语言:javascript
复制
sudo systemctl enable pvuv.service

前端显示
网站 PV UV

我选择在 Fluid 主题配置文件中加入该部分前端代码

打开 Hexo/_config.fluid.yml 文件

关闭原始 PV、UV 统计

代码语言:javascript
复制
  
# 展示网站的 PV、UV 统计数
# Display website PV and UV statistics
statistics:
  enable: false
  
  # 统计数据来源,使用 leancloud 需要设置 `web_analytics: leancloud` 中的参数;使用 busuanzi 不需要额外设置,但是有时不稳定,另外本地运行时 busuanzi 显示统计数据很大属于正常现象,部署后会正常
  # Data source. If use leancloud, you need to set the parameter in `web_analytics: leancloud`
  # Options: busuanzi | leancloud
  source: "leancloud"
  
  pv_format: "总访问量 {} 次"
  uv_format: "总访客数 {} 人"

由于 LeanCloud 仅在大陆无法访问,国外网友访问时还是会正常显示一行 PV,UV 统计,为了避免重复把原来的关掉

在 footer.content 中加入前端代码

代码语言:javascript
复制
<div style="font-size: 0.85rem;">
    <span> 总访问量 <span style="color: #d7d8d9;" id="PVstatic">0</span> 次&nbsp</span>
    <span> 总访客数 <span style="color: #d7d8d9;" id="UVstatic">0</span> 人&nbsp</span>
    <span> 当前在线 <span style="color: #d7d8d9;" id="ACTstatic">0</span> 人&nbsp</span>
    <script src="https://<url-to-app>/statistics" defer></script>
</div>

post 文章 PV UV

修改 fluid 主题配置文件 Hexo/_config.fluid.yml,加入新的文章浏览计数来源,我起名叫 vvdpostpvuv

代码语言:javascript
复制
# 浏览量计数
# Number of visits
views:
  enable: true
  # 统计数据来源
  # Data Source
  # Options: busuanzi | leancloud | 自建基于 Umami 统计的 文章PV vvdpostpvuv
  source: "vvdpostpvuv"
  format: "{} 次"

打开文件 Hexo/themes/fluid/layout/_partials/post/meta-top.ejs, 加入新的 else 分支:

代码语言:javascript
复制
    <% } else if (theme.post.meta.views.source === 'busuanzi')  { %>
      <span id="busuanzi_container_page_pv" style="display: none">
        <i class="iconfont icon-eye" aria-hidden="true"></i>
        <%- views_texts[0] %><span id="busuanzi_value_page_pv"></span><%- views_texts[1] %>
      </span>
      <% import_js(theme.static_prefix.busuanzi, 'busuanzi.pure.mini.js', 'defer') %>
    <% } else if (theme.post.meta.views.source === 'vvdpostpvuv')  { %>
      <span id="vvdpost_container_page_pvuv" style="display: none">
        <i class="iconfont icon-eye" aria-hidden="true"></i>
        <%- views_texts[0] %><span id="vvdpost_value_page_pv">0</span><%- views_texts[1] + '&nbsp&nbsp' %>
  <i class="iconfont icon-users" aria-hidden="true"></i>
  <%- views_texts[0] %><span id="vvdpost_value_page_uv">0</span><%- ' 人' %>
      </span>
  
<script>
	console.log(window.location.pathname)
	var httpRequest = new XMLHttpRequest();
	httpRequest.open('POST', 'https://101.43.39.125:6101/poststats', true);
	httpRequest.setRequestHeader("Content-type","application/json");
	httpRequest.send(JSON.stringify(window.location.pathname));
	httpRequest.onreadystatechange = function () {//请求后的回调接口,可将请求成功后要执行的程序写在其中
		if (httpRequest.readyState == 4 && httpRequest.status == 200) {//验证请求是否发送成功
			var json = httpRequest.responseText;//获取到服务端返回的数据
			var obj = JSON.parse(json);
			console.log(obj.pv);
			var pvCtn = document.querySelector('#vvdpost_container_page_pvuv');
			console.log(pvCtn);
			if (pvCtn) {
				var pv_ele = document.querySelector('#vvdpost_value_page_pv');
				console.log(pv_ele);
				var uv_ele = document.querySelector('#vvdpost_value_page_uv');
				console.log(uv_ele);
				if (uv_ele && uv_ele) {
					pv_ele.innerText = obj.pv;
					uv_ele.innerText = obj.uv;
					pvCtn.style.display = 'inline';
					}
				}
			}
	};
</script>
    <% } %>

https 访问
  • Nginx 做 ssl 反向代理,给出 https 访问链接、端口

效果展示

  • 站点 PV UV
  • Post PV UV

参考资料

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年8月15日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • 功能需求
  • 原理思路
    • 计数工具
      • LeanCloud 数据继承
        • 后台代码
          • 前端显示
            • https 访问
              • 插入 Fluid
              • 实现流程
                • 计数工具
                  • LeanCloud 数据继承
                    • 后台代码
                      • 核心代码
                      • 后端代码
                      • 搭建服务
                    • 前端显示
                      • 网站 PV UV
                      • post 文章 PV UV
                    • https 访问
                    • 效果展示
                    • 参考资料
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档