- 破解了上万个小程序,并上架的服务商-笨淘淘。以及背后的公司武汉X源举报侵权居然没有用?
武汉X源科技有限公司破解了上万个小程序,利用之前服务商的漏洞,批量注册小程序,并在昆明官渡区,沈阳,武汉,山西晋城市等地方批量注册数千家公司和个体户。破解其他开发者的心血,批量上架并霸榜。快去搜搜你的小程序有没有被破解并重新上架。关键是由于被破解的大部分是小开发者,没有申请软著,投诉并没有用。 如果你搜索下面的小程序,显示已经暂停服务,因为开发者的愤怒让他们害怕,暂停了一部分小程序,并且取消了小程序和服务商的关联,但是这远远不够,我们希望官方能惩罚他们。--10月17号晚上22:35分更新 文章分成三部分:1、破解证据;2、上架了多少产品;3、注册了多少公司。 1、部分破解证据 下面以吉祥日历和文学典读,还发现有今日菜价等等。持续更新中。 以破解小程序吉祥日历为例: [图片] [图片] 笨淘淘的套路就是批量破解,首页名字都没有改,加上视频广告和各种插屏广告,还接入第三方的谷歌广告。批量上线。 由于他们盗版的都是小开发者,并没有软著,举报代码侵权,并没有用。 更让人感觉讽刺的是这种满眼广告的小程序用户比正版小程序用户多的多! 看到有人说这个。不算,但是各位如果你们搜索过就会发现, 左上角的logo是一样, 顶部的名字是一样,UI一摸一样,甚至bug也一样,唯一的差别就是对方在顶部注入了视频广告,导致视觉上看起来有点不像! 下面这个文学典读: 对方破解后就是在顶部加视频广告,连搜索结果都一摸一样。 底部的文章顺序也一摸一样。 详情页就是添加了视频广告。 从内容到UI都是一样。 原版:干净整洁 [图片] 盗版:批量插入广告,定时弹出插屏广告,动不动就有激励视频。 而且文章和诗词的顺序一摸一样,甚至连搜索的内容都是一致的。 更恐怖的是当搜索相关关键词时候,大部分都是盗版小程序。 [图片] 对方上线了多少个类似古诗词的应用。 以下的小程序仅仅为古诗必备的前30搜出来的应用。如果考虑到长尾词。只会更多。 [图片] [图片][图片][图片][图片][图片][图片] 2、上架了多少产品; 以上仅仅是其中两个例子。还有各类群工具,转盘,扫描,记账、表情包,日历,五行穿衣,变声器,压缩,拼图,各类小游戏,游戏助手等工具。 可以看到有的小程序上甚至不加掩饰,连名字都没有改。更讽刺的是盗版的用户比正版用户多的多。 盗版小程序破千的应用比比皆是。 通过关键词霸榜,蹭名字,蹭图标混淆小程序,快速提量,并且通过官方广告+第三方广告快速变现,完全不管用户体验。点击一个按钮就要激励视频。 极大的破坏了生态。 下面以扫描王关键词,统计前70有多少个笨淘淘服务商相关的小程序。 每个小程序都是一样,并没有差异化体验,而且小程序体验非常糟糕,广告满天飞。动不动强制用户看激励视频。以下截屏,是按照顺序截图,统计了前70个小程序,其中29个是笨淘淘相关小程序,占比41%。 每个都一摸一样。 到处是广告,动不动就弹广告。 [图片] [图片] [图片] 如果搜索更加一些细分类型的关键词,结果也很多。 比如变声器,转盘。 如下图的变声器,占比达到40%。 [图片][图片] 社区有人统计出一部分,比如官渡区,然而这仅仅是一部分,还有沈阳市苏家屯区和海口部分地区。 根据他们的小程序序号,可能有40万个。 他们的小程序一开始名字都叫 “数字+试用小程序”, 下面这个小程序,是427970。如果排序正确,就是说下面这个小程序是第427970个小程序。 [图片] 如何识别这些小程序,打开小程序,更多资料,查看授权第三方服务, 只要是笨淘淘的就是他们的小程序。 2023年10月17号发现,对方已经在取消授权第三方服务。 如果要确实是否是笨淘淘的小程序,需要看到具体公司,然后到爱企查,天眼查,查看法人,如果是赵婷婷或者是陈池,八九不离十。 3、注册了多少公司 这些公司大多以武汉X源科技有限公司的老板陈池和赵婷婷为主。 要注意很多新公司没法被关联的。所以这里现实330仅仅是其中一部分。 [图片] [图片] [图片] [图片] [图片] [图片] 要注意,这里都仅仅是一部分。 很多公司并没有相互关联,如下的这一家。 [图片] 这个公司的小程序是这个画风,清一色破解批量上架小程序。 [图片] 看到社区有人反馈,但这仅仅是一部分。下面是链接。 https://developers.weixin.qq.com/community/develop/doc/00002c8efd0b70d3f6306550761800?highLine=%25E5%25AE%2598%25E6%25B8%25A1%25E5%258C%25BA 一个武汉X源科技的陈年往事。 这张图是2023年7月6号截图。 当时发现自己有打牌记分的需求。看到分数异常截图下来。没想到还有用。 当时是武汉X源做了个打牌记账工具和排名第一个的“打牌记账”UI一摸一样,“打牌记账|计分器”短时间内就排名靠前,并且评分 5.0分, 而打牌记账 分数3.6分。今天,已经完全找不到武汉X源的“打牌记账”小程序了,不知道当时收到官方什么惩罚。 但是,笨淘淘系上线了一系列新的“打牌记账”类应用, UI已经和当初抄袭的完全不同。如果被逃过,估计他们就会给破解的小程序套上新的UI。摇身一变。 再次希望对方能得到应有的惩罚。 [图片] 如果批量破解上架举报没有用,那大家开发什么,批量上架就行了。请官方严肃处理这个服务商。严惩相关人员。
2023-10-30 - 云开发云函数中使用Redis的最佳实践,包括五种常用数据结构和分布式全局锁
Redis因其拥有丰富的数据结构、基于单线程模型可以实现简易的分布式锁、单分片5w+ ops的超强性能等等特点,成为了大家处理高并发问题的最常用的缓存中间件。 那么云开发能不能使用Redis呢?答案是肯定的。 下面我介绍下云开发中Redis使用的最佳实践: 第一步、购买Redis,安装Redis扩展 参见官方文档:https://developers.weixin.qq.com/community/develop/article/doc/000a4446518488b6002c9fa3651813 吐槽一下,写这篇文章的原因之一就是上面的官方文档中的示例代码是在不堪入目,希望这篇文章能让小伙伴少踩些坑。 第二步、创建并部署测试云函数,配置云函数的网络环境 [图片] 第三步、编写代码 cache.js const Redis = require('ioredis') const redis = new Redis({ port: 6379, host: '1.1.1.1', family: 4, password: 'password', db: 0 }) exports.redis = redis /** * 加redis全局锁 * @param {锁的key} lockKey * @param {锁的值} lockValue * @param {持续时间,单位s} duration */ exports.lock = async function(lockKey, lockValue, duration) { const lockSuccess = await redis.set(lockKey, lockValue, 'EX', duration, 'NX') if (lockSuccess) { return true } else { return false } } /** * 解redis全局锁 * @param {锁的key} lockKey * @param {锁的值} lockValue */ exports.unlock = async function (lockKey, lockValue) { const existValue = await redis.get(lockKey) if (existValue == lockValue) { await redis.del(lockKey) } } 上面是操作redis的工具方法,可以打包放到云函数的层管理中,方便其他云函数引用。层管理使用方式参见官方文档:https://cloud.tencent.com/document/product/876/50940 index.js const cloud = require("wx-server-sdk") const cache = require('/opt/utils/cache.js') // 使用到了云函数的层管理 cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) global.cloud = cloud global.db = cloud.database() global._ = db.command global.$ = _.aggregate exports.main = async (event, context) => { context.callbackWaitsForEmptyEventLoop = false const wxContext = cloud.getWXContext() let appId = wxContext.APPID if (wxContext.FROM_APPID) { appId = wxContext.FROM_APPID } let unionId = wxContext.UNIONID if (wxContext.FROM_UNIONID) { unionId = wxContext.FROM_UNIONID } let openId = wxContext.OPENID if (wxContext.FROM_OPENID) { openId = wxContext.FROM_OPENID } // redis五种常用数据结构 // 字符串 await cache.redis.set('hello', 'world') // 无过期时间 await cache.redis.set('hello', 'world', 'EX', 60) // 过期时间60s let stringValue = await cache.redis.get('hello') console.log('string: ', stringValue) // hash await cache.redis.hset('hash', 'hello', 'world') let hashValue = await cache.redis.hget('hash', 'hello') console.log('hash: ', hashValue) // list await cache.redis.lpush('list', 'hello', 'world') let listList = await cache.redis.lrange('list', 0, -1) // 读取队列所有元素 await cache.redis.ltrim('list', 1, 0) // 清空队列 console.log('listList: ', listList) // set await cache.redis.sadd('set', 'hello', 'world') let setExist = await cache.redis.sismember('set', 'hello') // 检查元素是否在集合中 console.log('set: ', setExist) // zset await cache.redis.zadd('zset', 1, 'hello', 2, 'world') let zsetList = await cache.redis.zrange('zset', 0, -1, 'WITHSCORES') console.log('zsetList: ', zsetList) // redis实现分布式全局锁 // 加全局锁,锁的过期时间应根据实际业务调整 const createOrderLock = `createOrderLock:${unionId}` const ts = Date.now() if (!(await cache.lock(createOrderLock, ts, 3))) { return { code: 4, msg: '操作太频繁了' } } // 这边写全局互斥的业务逻辑代码 // 比如创建订单,一个用户同时只能并发创建一个订单 // 解全局锁 await cache.unlock(createOrderLock, ts) return { code: 0, data: {} } } 上面是测试云函数的入口文件,演示了redis五种常用数据结构和redis全局锁的使用方法。 最后还有个小tips,所有引用到cache.js的云函数需要安装ioredis的依赖,进入云函数目录,使用如下命令: npm install ioredis
2021-06-17 - 想增加个group输出字段怎么弄?
在TD集合中搜索班级为jl1的数据jl1各个数据中包括score[]求和已经实现,但是输出想增加下group字段 const db=wx.cloud.database() const $ = db.command.aggregate const _=db.command db.collection('TD').aggregate().match({ clas: _.eq('jl1') }).unwind('$score').group({ _id:'$_id', sum:$.sum('$score') }) . end() .then(res=>{ console.log(res) }) [图片]
2021-02-19 - 复杂瀑布流长列表页踩坑记录,内存不足问题【2】
第二期来啦,带来了新的方案和代码片段~ 第一期点此查看 上期问题 经过一系列的实践,上期的方案有些问题,其中最麻烦的就是,需要对外传递一个当前index,然后控制前后数据展示;这里对于每个用到[代码]skeleton[代码]组件的页面来说,都要重复的写一个方法来承接这个index,然后渲染页面对应的数据。 优化 依然是监听[代码]skeleton[代码]曝光,这里监听的方案变为出现在屏幕上下[代码]n[代码]屏的内容块进行展示,此范围外的内容块就卸载掉。 核心代码 [代码] // 修改了监听是否显示内容的方法,改为前后showNum屏高度渲染 // 监听进入屏幕的范围relativeToViewport({top: xxx, bottom: xxx}) let info = SystemInfo.getInfo() //获取系统信息 let { windowHeight = 667 } = info.source.system let showNum = 2 //超过屏幕的数量,目前这个设置是上下2屏 let listItemContainer = this.createIntersectionObserver() listItemContainer.relativeToViewport({ top: showNum * windowHeight, bottom: showNum * windowHeight }) .observe(`#list-item-${this.data.skeletonId}`, (res) => { // 此处来控制slot展示,详见代码片段 }) [代码] 干货 话不多说,干货放后面,点击获取代码片段
2019-12-05 - 小程序粘性布局组件实现
一、前言 开发中,我们经常会遇需要让组件在屏幕范围内时,按照正常布局排列,而组件滚出屏幕范围时,让其始终固定在屏幕顶部的情况,也就是常说的粘性布局。今天我们就一起用小程序来实现一个适用于不同场景下的粘性布局组件。 二、demo演示 如图,实现的组件主要适用于以下几种场景: 吸顶页面最上方; 吸顶与页面有固定距离的位置; 在指定容器内吸顶; 嵌套在scroll-view中吸顶。 [图片] 三、代码演示 其中,粘性组件通过<weimob-sticky></weimob-sticky>调用,参数信息用法如下: 参数 说明 类型 默认值 offset-top 吸顶时与顶部的距离,单位px number 0 z-index 吸顶时的 z-index number 99 container 一个函数,返回容器对应的 NodesRef 节点 function - scroll-top 当前滚动区域的滚动位置,非 null 时会禁用页面滚动事件的监听 number - 滚动时触发scroll函数,其中isFixed为是否吸顶,scrollTop为距离顶部的位置。详细代码如下。 3.1 页面代码 3.1.1 基础用法 [代码]<view class="weimob-block"> <view class="weimob-title">基础用法</view> <view class="weimob-body"> <weimob-sticky> <!-- 需要粘性的部分 --> <button class="margin-left-base" size="mini"> 基础用法 </button> </weimob-sticky> </view> </view> [代码] 3.1.2 吸顶距离 [代码]<view class="weimob-block"> <view class="weimob-title">吸顶距离</view> <view class="weimob-body"> <!-- 吸顶时与顶部的距离,单位px --> <weimob-sticky offset-top="{{ 50 }}"> <!-- 需要粘性的部分 --> <button class="margin-left-top" type="primary" size="mini"> 吸顶距离 </button> </weimob-sticky> </view> </view> [代码] 3.1.3 指定容器 [代码]<view class="weimob-block"> <view class="weimob-title">指定容器</view> <view class="weimob-body"> <!-- 这里需要固定高度 --> <view id="container" style="height: 300rpx;background-color: #fff"> <weimob-sticky container="{{ container }}"> <button size="mini" class="margin-left-special"> 指定容器 </button> </weimob-sticky> </view> </view> </view> [代码] 3.1.4 嵌套在scroll-view使用 [代码]<view class="weimob-block"> <view class="weimob-title">嵌套在 scroll-view 内使用</view> <!-- 这里需要固定高度,scroll-view里的元素高度需要大于其高度 --> <scroll-view bind:scroll="onScroll" scroll-y id="scroller" style="height: 400rpx; background-color: #fff;margin-top: 40rpx;" > <view style="height: 800rpx"> <weimob-sticky scroll-top="{{ scrollTop }}" offset-top="{{ offsetTop }}" > <button size="mini" class="margin-left-scoll"> 嵌套在 scroll-view 内 </button> </weimob-sticky> </view> </scroll-view> </view> [代码] 页面js [代码]Page({ data: { container: null, //一个函数,返回容器对应的 NodesRef 节点 scrollTop: 60, // 当前滚动区域的滚动位置,非null时会禁用页面滚动事件的监听 offsetTop: 0 // 吸顶时与顶部的距离,单位px }, onReady() { // 页面渲染完,获取节点信息 this.setData({ container: () => wx.createSelectorQuery().select('#container'), }); }, onScroll(event) { // 容器滚动时获取节点信息 wx.createSelectorQuery() .select('#scroller') .boundingClientRect((res) => { this.setData({ scrollTop: event.detail.scrollTop, offsetTop: res.top, }); }) .exec(); } }); [代码] 3.2 组件代码 组件wxml [代码]<wxs src="./index.wxs" module="computed" /> <view class="weimob-sticky" style="{{ computed.containerStyle({ fixed, height, zIndex }) }}" > <view class="{{ fixed ? 'weimob-sticky-wrap--fixed' : ''}}" style="{{ computed.wrapStyle({ fixed, offsetTop, transform, zIndex }) }}" > <slot /> </view> </view> [代码] 组件wxs 这里使用使用小程序的wxs对吸顶元素的transform,top,height,z-index元素进行实时渲染,ios设备在滚动监听时性能会优于在js 2-20倍,androd设备效率暂无差异。 [代码]function wrapStyle(data) { var style = ""; if (data.transform) { style += 'transform: translate3d(0, ' + data.transform + 'px, 0);' } if (data.fixed) { style += 'top: ' + data.offsetTop + 'px;' } if (data.zIndex) { style += 'z-index: ' + data.zIndex + ';' } return style; } function containerStyle(data) { var style = ""; if (data.fixed) { style += 'height: ' + data.height + 'px;' } if (data.zIndex) { style += 'z-index: ' + data.zIndex + ';' } return style; } module.exports = { wrapStyle: wrapStyle, containerStyle: containerStyle } [代码] 组件js [代码]import pageScrollMixin from "./page-scroll"; const ROOT_ELEMENT = ".weimob-sticky"; Component({ options: { multipleSlots: true }, properties: { zIndex: { type: Number, value: 99 }, offsetTop: { type: Number, value: 0, observer: "onScroll" }, disabled: { type: Boolean, observer: "onScroll" }, container: { type: null, observer: "onScroll" }, scrollTop: { type: null, observer(val) { this.onScroll({ scrollTop: val }); } } }, data: { height: 0, fixed: false, transform: 0 }, behaviors: [pageScrollMixin(function pageScrollMixinCallback(event) { // 非null时会禁用页面滚动事件的监听 if (this.data.scrollTop != null) { return; } this.onScroll(event); })], lifetimes: { attached() { this.onScroll(); } }, methods: { onScroll({ scrollTop } = {}) { const { container, offsetTop, disabled } = this.data; if (disabled) { this.setDataAfterDiff({ fixed: false, transform: 0 }); return; } this.scrollTop = scrollTop || this.scrollTop; if (typeof container === "function") { // 情况一:指定容器下时,吸顶距离+吸顶元素高度>容器高度+容器距顶部距离,随页面滚动; // 情况二:指定容器下时,吸顶距离>吸顶元素高度,元素固定; // 情况三:元素初始化。 // this.getRect获取节点ROOT_ELEMENT相对于显示区域的top,height等信息,通过root获取 // this.getContainerRect获取父容器相对于显示区域的top,height等信息,通过container获取 Promise.all([this.getRect(ROOT_ELEMENT), this.getContainerRect()]).then( ([root, container]) => { if (offsetTop + root.height > container.height + container.top) { this.setDataAfterDiff({ fixed: false, transform: container.height - root.height }); } else if (offsetTop >= root.top) { this.setDataAfterDiff({ fixed: true, height: root.height, transform: 0 }); } else { this.setDataAfterDiff({ fixed: false, transform: 0 }); } }); return; }else{ this.getRect(ROOT_ELEMENT).then(root => { // 吸顶时与顶部的距离小于可视区域的top距离时,随着滚动条滚动,否则吸顶 if (offsetTop >= root.top) { this.setDataAfterDiff({ fixed: true, height: root.height }); this.transform = 0; } else { this.setDataAfterDiff({ fixed: false }); } return Promise.resolve(); }); } }, setDataAfterDiff(data) { // 比较数据是否与上次相同,不同则触发父组件scroll事件更新isFixed,scrollTop。 wx.nextTick(() => { const diff = Object.keys(data).reduce((prev, key) => { const prevCopy = prev; if (data[key] !== this.data[key]) { prevCopy[key] = data[key]; } return prevCopy; }, {}); this.setData(diff); this.triggerEvent("scroll", { scrollTop: this.scrollTop, isFixed: data.fixed || this.data.fixed }); }); }, getContainerRect() { const nodesRef = this.data.container(); return new Promise(resolve => nodesRef.boundingClientRect(resolve).exec()); }, getRect(selector) { return new Promise(resolve => { wx.createSelectorQuery().in(this).select(selector).boundingClientRect(rect => { resolve(rect); }).exec(); }); } } }); [代码] page-scroll.js 滚动事件在页面进入和离开时共享的pageScrollMixin函数。 [代码]function getCurrentPage() { const pages = getCurrentPages(); return pages[pages.length - 1] || {}; } function onPageScroll(event) { const { weimobPageScroller = [] } = getCurrentPage(); weimobPageScroller.forEach(scroller => { if (typeof scroller === "function" && event) { // @ts-ignore scroller(event); } }); } const pageScrollMixin = scroller => Behavior({ attached() { const page = getCurrentPage(); if (Array.isArray(page.weimobPageScroller)) { page.weimobPageScroller.push(scroller.bind(this)); } else { page.weimobPageScroller = typeof page.onPageScroll === "function" ? [page.onPageScroll.bind(page), scroller.bind(this)] : [scroller.bind(this)]; } page.onPageScroll = onPageScroll; }, detached() { const page = getCurrentPage(); page.weimobPageScroller = (page.weimobPageScroller || []).filter(item => item !== scroller); } }); export default pageScrollMixin; [代码] 总结 最后,我将上述代码放在了代码片段中供大家使用了解,https://developers.weixin.qq.com/s/qiym3wmr7znx ,希望能够帮到小伙伴们,欢迎评论区建议或指教哦~
2021-01-26 - 小程序利用云开发实现图片安全检测(imgSecCheck,msgSecCheck)
最近在做一款拍照的小程序,审核时被拒绝,需要增加云安全检测,使用的方法流程是,小程序使用云上传,云平台下载图片在云检测,将检测结果返回给小程序,小程序通过结果来判断是否安全 // 小程序端 // 主方法 handleCamera: function () { let that = this this.selectImg().then(fileImg => { wx.showLoading({ title: '分析中', mask: true }) wx.cloud.uploadFile({ cloudPath, filePath: fileImg, success(res) { wx.cloud.callFunction({ name: "imgSecCheck", data: { fileID: res.fileID } }).then(res => { if (res.result.errCode !== 0) { wx.showToast({ title: '违规图片,请重新上传', icon: 'none' }) } else { wx.hideLoading() that.jumptoresult() } }).catch(err => { wx.showToast({ title: '违规图片,请重新上传', icon: 'none' }) console.log(err) }) }, fail(err) { console.log(err) wx.showToast({ title: '上传失败', icon: 'none' }) } }); }) }, // 选择图片 selectImg: function () { if(!this.data.isShowAdv){ return } let that = this return new Promise((resolve, reject) => { wx.chooseImage({ count: 1, sizeType: ["compressed"], sourceType: ["camera", "album"], success: function (chooseFile) { that.setData({ imagex: chooseFile.tempFiles[0].path, }) resolve(chooseFile.tempFilePaths[0]) }, }) }) } // 云开发端 // 云函数入口文件 const cloud = require('wx-server-sdk') cloud.init() // 云函数入口函数 exports.main = async(event, context) => { const fileID = event.fileID const res = await cloud.downloadFile({ fileID: fileID, }) const buffer = res.fileContent try { var result = await cloud.openapi.security.imgSecCheck({ media: { contentType: 'image/png', value: buffer } }); return result } catch (err) { return err } } 试了一些其它方案没走下去,我列举下大家可以参考 1、用户拍完照使用云安全检测报错提示 data maxsize 等等 因为云安全检测的大小限制为1M 我们手机多数拍出来都比较大,所以更换了文中那种方案 2、使用canvas画图把图片搞小,然后在安全检测 失败告终,图片小了可以上传,但安全检测也识别不出来了 还有一个云文本安全检测,用法同云安全图片比图片还简单一些,大家改改就行,需要帮助也可以私聊我 下面是成品,可扫码测试 [图片]
2021-10-07 - 内容安全检测图片API:openapi.security.imgSecCheck完美解决方案。
背景需求: 我个人做了一款小程序的小游戏,本质是小程序。里面有个自定义图片的功能。用户从本地相册选一张图片进行裁剪,之后保存到缓存中或者上传到服务器。然后用户再用这张图片作为素材进行其它操作。这里就涉及到内容安全了,提交审核没有通过也是因为这个没有做内容安全。防止一些色情低俗的事情发生。 正文: 思路:相册选图片 --> 裁剪小的图片 --> 内容安全检测 --> 通过 --> 裁剪大的图片 --> 保存。 失败的原因:绝大多数是因为检测图片不能大于1M,而导致超时,或者是errCode:-1,又或者是其它问题。 [图片] [图片] 核心代码图片: [代码]默认裁剪小尺寸图片 (我的业务需求是正方形图片,也可动态计算宽高比例) [代码] [图片] 检测图片 部分iOS不兼容encoding: ‘ucs2’。注释掉就好了 [图片] [图片] 云函数 [图片] 测试情况: 正常图片不含违法违规,测试20次,全部通过。小程序上线后暂无发现检测失败情况。百度搜索的“人体油画”等等均可通过。 PS:第一次写经验分享哈,看不懂可以问我。体验一下我的小程序想问我这个小程序其它的功能点也可以喔! 技术会迭代更新,用到的技术会有时效性,看编辑时间,可能当时的技术现在不适用了
2020-10-22 - 首次调用云函数特别慢,怎么解决?
云函数只是一个很简单的加解密工具类,由于把密钥放在前端不安全,就打算把密钥和加密算法写在云函数中。 但是首次调用云函数慢的令人发指,在开发工具上倒还好,勉强能接受 [图片] 但是到手机上调用首次基本需要1s左右,然后后续接连调用就会很快了100-200ms左右吧。但是过一段时间再次调用又会出现1s左右的等待。 然后云函数调用日志中实际运行之间只有 2-3ms。 有什么好的建议吗 06/30 15:05:08:192 [KkcRsa][2] START 06/30 15:05:08:361 [KkcRsa][2] END 06/30 15:05:08:361 [KkcRsa][2] REPORT RequestId:80e256bc-d971-11eb-a778-525400e5615d Duration:3ms Memory:128MB MemUsage:12.101562MB 06/30 15:16:21:039 [KkcRsa][1] START 06/30 15:16:21:435 [KkcRsa][1] END 06/30 15:16:21:435 [KkcRsa][1] REPORT RequestId:11ec1fe4-d973-11eb-975b-525400c2bfee Duration:2ms Memory:128MB MemUsage:12.429688MB 06/30 15:17:01:080 [KkcRsa][0] START 06/30 15:17:01:445 [KkcRsa][0] END 06/30 15:17:01:445 [KkcRsa][0] REPORT RequestId:29cc1ac9-d973-11eb-975b-525400c2bfee Duration:1ms Memory:128MB MemUsage:12.750000MB
2021-06-30 - 小程序搜索优化指南(SEO)
2019年上半年微信发布了基于小程序页面的搜索,为了让我们更好地发现及理解小程序的页面,结合过去一段时间来我们遇到的各种情况,我们强烈建议各位开发者花一些宝贵的时间认真阅读本文:) 爬虫访问小程序内页面时,会携带特定的 user-agent "mpcrawler" 及场景值:1129 1. 小程序里跳转的页面 (url) 可被直接打开。 小程序页面内的跳转url是我们爬虫发现页面的重要来源,且搜索引擎召回的结果页面 (url) 是必须能直接打开,不依赖上下文状态的。特别的:建议页面所需的参数都包含在url 2. 页面跳转优先采用navigator组件。 小程序提供了两种页面路由方式: a.navigator 组件 b. 路由 API,包括 navigateTo / redirectTo / switchTab / navigateBack / reLaunch 建议使用 navigator 组件,若不得不使用API,可在爬虫访问时屏蔽针对点击设置的时间锁或变量锁。 3.清晰简洁的页面参数。 结构清晰、简洁、参数有含义的 querystring 对抓取以及后续的分析都有很大帮助,但是将 JSON 数据作为参数的方式是比较糟糕的实现。 4. 必要的时候才请求用户进行授权、登录、绑定手机号等。 建议在必须的时候才要求用户授权(比如阅读文章可以匿名,而发表评论需要留名)。 5. 我们不收录 web-view 中的任何内容。 我们暂时做不到这一点,长期来看,我们可能也做不到。 6. 利用 sitemap 配置引导爬虫抓取,同时屏蔽无搜索价值的路径。 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html 7. 设置一个清晰的标题和页面缩略图。 页面标题和缩略图对于我们理解页面和提高曝光转化有重要的作用。 通过wx.setNavigationBarTitle或 自定义转发内容onShareAppMessage对页面的标题和缩略图设置,另外也为 video、audio 组件补齐 poster /poster-for-crawler属性。 8. 使用页面路径推送能力 可极大丰富微信可以收录的内容,进而提高小程序内容的曝光机会。请参考: https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/search/search.submitPages.html
2020-01-14