java性能优化微信公众号_20200313 Node微信公众号开发 前文整合优化

优化 accessToken.js

该文件用于获取公众号凭证 access_token ,之前的代码如下:

var fs = require('fs')

var request = require('./request') // 将 request 封装成 Promise

module.exports = (config) => { // config 为配置文件,其中包含 appid 、appScrect ……

return new Promise((resolve, reject) => {

var currentTime = new Date().getTime() // 获取当前时间戳

var url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' + config.appId + '&secret=' + config.appScrect // 发起请求

// 判断是否需要更新 accessToken

if (config.setAccessToken.accessToken === '' || config.setAccessToken.time < currentTime) {

// 若本地文件不存在凭证或凭证过期则更新本地重新凭证

// config.setAccessToken 为本地文件键名

request.get(url).then((response) => {

var result = JSON.parse(response)

config.setAccessToken.accessToken = result.access_token

config.setAccessToken.time = new Date().getTime() + (parseInt(result.expires_in) - 200) * 1000 // 计算过期时间

fs.writeFile('./config.json', JSON.stringify(config), (error) => {

if (error) {

reject(error)

}

resolve(response.access_token) // 导出 access_token

})

})

} else {

// 若凭证未过期直接导出本地 access_token

resolve(config.setAccessToken.accessToken)

}

})

}

以上实现方式是将请求结果通过 fs 模块直接写入本地文件存储,这样会导致代码略显臃肿(我暂时无法评估优化的利与弊,至少从代码量上看),而且在动态写入的过程中会导致配置文件文本格式被压缩不宜阅读。所以我将 access_token 的保存方式由本地保存修改未缓存储存,当然这里使用的是前文提到的 memory-cache 缓存组件。(之所以想到用 memory-cache 缓存模块是通过 基于nodejs 的微信 JS-SDK 简单应用 的启发)

memory-cache 用法回顾:

存储缓存。参数分别为:键名、键值、到期时间、回调函数

cache.put('houdini', 'disappear', 100, (key, value) => { console.log(key + ' did ' + value) })

读取缓存。参数为目标缓存的键名

cache.get('foo')

以下是我的具体“优化”操作:

// 首先将 fs 模块替换为 memory-cache

var cache = require('memory-cache')

// 之后通过 cache.get 去判断是否需要更新 access_token

if (!cache.get('accessToken')) { // 如果 access_token 不存在则将请求结果保存进缓存

request.get(url).then((response) => {

var result = JSON.parse(response)

cache.put('accessToken', result.access_token, (result.expires_in - 200) * 1000)

resolve(result.access_token) // 导出 access_token

})

} else { // 否则直接导出缓存中的 access_token

resolve(cache.get('accessToken'))

}

通过一番改造,代码量从 51 行生生压缩到了 22 行!个人甚是满意。

封装爬虫爬取页面数据

之前封装的 reply.js 是通过请求 wp-json 接口获得站点数据,从而实现诸如“自动被动回复”类的功能,这不免是在某些程度上降低了站点安全性。

cheerio 用法回顾:

const cheerio = require('cheerio') // 首先,引入模块 const $ = cheerio.load(html) // 获取整个 html 节点 const eleUl = $.find('#fruits') // 找到 id 为 fruits 的节点

// cheerio 模块对节点的使用方法 jQuery 如出一辙

在对 reply.js 进行优化前,我需要先封装一个专门用于爬取数据的页面,我将其命名为 reptile.js ,这里用到了前文的 cheerio 爬虫模块。

reptile.js 内包含两个爬取方法,分别用于爬取文章列表以及爬取正文。代码如下:

var cheerio = require('cheerio') // 引入模块

var config = require('./config') // 引入配置模块

var request = require('./request') // 将 request 封装成 Promise

exports.list = (callback, page) => { …… } // 回调函数用于向外抛出结果,page 为列表页 path

exports.detail = (url, callback) => { …… } // 回调函数用于向外抛出结果,url 为详情页 path

exports.list

因为站点文章列表和分类文章列表链接格式有区别,我做了区别处理,先看下我的路径:

首先我将公共部分 https://www.sfatpaper.com/ 提取到配置文件中,之后根据路由的配置对不同页面进行请求响应:

// router.js

function routerTo (res, page) {

reptile.list((data) => {

res.render('list.html', { // 渲染页面

data: data.list

})

}, page)

}

router.get('/history', (req, res) => { // 历史文章

routerTo(res, 'page/1')

})

router.get('/travel', (req, res) => { // 旅行日记

routerTo(res, 'travel/page/1')

})

router.get('/diary', (req, res) => { // 日常杂记

routerTo(res, 'diary/page/1')

})

router.get('/code', (req, res) => { // 码农笔记

routerTo(res, 'code/page/1')

})

// reptile.js - reptile.list

// config.reptilePath 即配置文件中的 https://www.sfatpaper.com/

request.get(config.reptilePath + page).then((response) => {

var html = ''

html += response // 将页面数据存入变量 html

var $ = cheerio.load(html)

function reptileData () {

var arr = [] // 用于存放文章数据

$('article').each((index, element) => { // 根据节点 $('article') 循环出所有文章

// 拆分路径的作用主要用于百度站长平台移动适配的链接匹配功能

var str = $(element).find('.entry-title a').attr('href').split('/')

arr.push({

sort: $(element).find('.meta-category a').text(), // 文章分类

title: $(element).find('.entry-title a').text(), // 文章标题

year: str[3],

month: str[4],

path: str[5],

excerpt: $(element).find('.entry-content p').text(), // 文章简介

thumbnail: $(element).find('.post-thumbnail img').attr('src') // 文章封面图

})

})

return arr // 返回爬取最终数组,此时 arr 为创建好的数组对象

}

callback(reptileData())

})

// list.html

// 这里需要重新将对应文章的链接拼接成正确的地址

{{each data}}

%7B%7B%24value.thumbnail%7D%7D

{{$value.title}}

{{$value.excerpt}}

{{/each}}

到此位置,列表页的渲染工作就到此为止。此时我们可以通过点击直接获取到对应路径的参数,从而定位爬取文章页面。

exports.detail

// router.js

router.get('/detail', (req, res) => {

var query = req.query // 获取到请求参数,从而拼接对应文章路径

// config.reptilePath 即配置文件中的 https://www.sfatpaper.com/

posts.detail(config.reptilePath + query.year + '/' + query.month + '/' + query.path, (detail) => {

res.render('detail.html', { // 后端处理数据

title: detail.title,

excerpt: detail.excerpt,

content: detail.content,

thumbnail: detail.thumbnail

})

})

})

// reptile.js - reptile.detail

request.get(config.reptilePath + url).then((response) => {

var html = ''

html += response // 将页面内容存入变量 html

var $ = cheerio.load(html)'

var postDetail = {

title: $('.entry-title').text(), // 文章标题

excerpt: $('*[name="description"]').attr('content'), // 文章简介

content: $('.entry-content').html(), // 文章正文

thumbnail: $('.entry-content img').eq(0).attr('src') // 文章正文首图

}

callback(postDetail)

})

这里有一点暂时没有想到好的办法,那就是文章缩略图和文章简介都显示与原站点列表页,通过层层传递的方式很繁琐,所以我将简介存到了页面 description 中,而图片则直接选择调取文章内容首图而非列表页的缩略图。

优化被动关键词自动回复

我的自定义回复文件命名为 reply.js ,具体详情还请移步至前文,这里主要优化的地方是最新文章回复的功能,这个功能可以通过回复字母“n”以及点击菜单“文章”->"最新文章"触发。

先来看下我之前的实现方法吧(以下代码只针对是获取最新文章功能):

// config.webJson 原 wp-json 接口地址

request.get(config.webJson).then((data) => {

var contentArr = []

var items = JSON.parse(data) // 将数据转化为对象数组

contentArr.push({

Title: item[0].title.rendered, // 文章标题

Description: item[0].excerpt.rendered.replace(/]+>/g, ''), // 文章简介(replace 解决数据中 html 标签显示问题)

PicUrl: item[0].thumbnail, // 文章缩略图

Url: item[0].link // 文章链接

})

// replyType.js 整合了所有回复的种类,包括图文回复、文本回复、图片回复 ……

resultXml = replyType.graphicMsg(result, contentArr) // 向 replyType.js 抛出最新文章数据

})

而现在我利用之前开发好的 reptile.js 模块,我们可以省去对 JSON.parse 数据转化工作(爬取来的内容为纯文本,带 html 标签),对于数据的调用也更为灵活,不会再拘泥于接口中繁杂的层级命名。

代码优化如下:

reptile.list((data) => {

var contentArr = [{

Title: data[0].title, // 文章标题

Description: data[0].excerpt, // 文章简介

PicUrl: data[0].thumbnail, // 文章缩略图

Url: config.routerPath + 'detail?year=' + data[0].year + '&month=' + data[0].month + '&path=' + data[0].path // 文章链接(拼接路径,用法如前文)

}]

resultXml = replyType.graphicMsg(result, contentArr) // 向 replyType.js 抛出最新文章数据

}, 'page/1') // 始终获取第一页的第一篇文章

怎么样,是不是看着多少顺眼了些呢?

前文还提到了另外一个模块 cron ,它是一个定时器用来定时去执行某样任务。起初是想利用该模块实现主动发送最新文章的功能,但由于种种原因该功能暂时被我放弃了。所以在这里也没有太多可以介绍的,若想要了解它还请异步至 cron 。

相关文章:

资料参考:

本文由博客一文多发平台 OpenWrite 发布!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值