策划
最近和我们组几位老师茶余饭后都在讨论这个github的中国区的关注者数量排名,第一名尤雨溪:7.4w关注者,第二名阮一峰:6.7w关注者,第三名廖雪峰:3.4w关注者。
另外:尤雨溪大佬是中国区第一,世界第二。世界第一是Linux的创始人Linus大神
市面上虽然已经有不少做这个排名统计的网站,但我总觉得缺点什么,比如:
- 历史统计
如果时间够长,我们甚至可以恢复出一个中国区开源社区的里程图,只有我自己爬取数据才做得到,不然只能等别人做。
- 查看自己的排名并生成海报
想要分享自己的排名只能截图,不如我自己生成一个精美的海报。
设计
找一个适合的风格,参考着布局设计。作为一个经验老道的前端,看到各种设计的时候,脑海中就已经浮现出css
了吧?
爬虫方案选型
本着一贯白嫖的原则,这个爬虫我可不想用自己的服务器,得尽可能的让其在一个免费稳定的云服务中定期运行。
- 方案1:uniCloud云服务
优点 | 可定时任务,可直接连接云数据库,云存储 |
缺点 | 超时时间只有60s,而github千条数据的爬虫,需要2分钟左右爬完。并且从国内服务器访问github api比较不稳定。 |
- 方案2:github actions
优点 | 可定时任务,用github的服务爬取github数据速度极快 |
缺点 | 无法直接关联我在uniCloud里的云数据库 |
由于方案一的超时时间
是固定的又比较短,坚持用它虽然也能设计出一个爬取方案,但是太麻烦了。正好借这个机会好好学习下GithubActions
,爬取完成将数据传回uniCloud云函数,再通过云函数去录入数据库不就好了吗。
弄它
nodejs
里调用github api比较简单,唯一需要注意两点。
- 用户搜索最多只能1000条数据
- 访问频率
基础授权模式下每分钟只允许个位数的请求,我们要爬取前1000名,这个是不够的,需要我们去Github设置中生成自己的TOKEN,使用TOKEN的请求频率一下子来到每分钟5000次,这下完全足够了。
将自己的token带入请头
async function githubApiGet(url,data){
if(!data)data={};
let res;
try{
res = await axios.get(url,{
headers:{
"Authorization":'Token '+token,
"Accept":"application/vnd.github.v3+json"
},
params:data
})
}catch(err){
console.log(err);
}
return res;
}
由于我要把中国区的排名历史都记录下来,未来这个仓库的文件数量可能无比多。因为每天会生成一个。所以得设计一下目录结构
把年份,月份拆分成独立目录,这样的话,单目录里文件最多也就31个。
Github Actions
配置项的用途请看代码块中的注释,这里只讲我用到的参数。
# 这个工作流的名称
name: ROCSchedule
on:
# 定期任务,用的是国际标准时间,0 18这个设定代表中国的凌晨2点
schedule:
- cron: '0 18 * * *'
# 是否允许在仓库面板手动触发工作流,允许。
workflow_dispatch:
jobs:
build:
# 运行的Linux系统
runs-on: ubuntu-latest
steps:
# 从仓库取出代码
- uses: actions/checkout@v2
# 设置node环境
- name: Setup Node.js environment
uses: actions/setup-node@v2.4.0
# 执行npm install安装依赖
- name: Install NPM dependencies
run:
npm install axios
# 把我们的代码运行起来
- name: Run
run:
# 携带必要的参数运行,这里的MYTOKEN是解决github api请求频率问题的,POSTURL是爬取完成后发送数据的接口URL
MYTOKEN=${{secrets.MYTOKEN}} POSTURL=${{secrets.POSTURL}} node index.js
- name: Add & Commit
# 由于我们会在仓库里生成一个json文件,所以还需要把仓库push一下
uses: EndBug/add-and-commit@v7.3.0
with:
github_token: ${{secrets.MYTOKEN}}
Secrets
我的 MYTOKEN和POSTURL 不应该被泄露,但我又在项目中用到了,那怎么开源呢?GithubActions的Secrets变量太好用了,这样我既可以开源整个项目,但又不会泄露自己的隐私数据。每个fork的用户也都可以使用自己的TOKEN来搭建服务
uniCloud云函数接收POST
新建一个uniCloud云函数,开启URL化。
云函数URL化后接收POST过来的参数都在event.body
中,用JSON解析一下就可以用了
if(event.body){
//github action post
const data = JSON.parse(event.body);
await db.collection('githubroc').add({
record_date:data.record_date,
total_users:data.total_users,
rank_list:data.rank_list
});
return;
}
获取云数据库中的排行数据
我们通过当前日期 2021-09-11 这样的格式去查询 数据库 中的匹配项。如果没有匹配到指定日期的话,则取最新一条。这里由于必然存在至少一条,所以我就不做没有的判断了
const date = event.date;
dbRes = await db.collection('githubroc').where({
record_date:dbCmd.eq(date)
}).get();
if(dbRes.affectedDocs<=0){
dbRes = await db.collection('githubroc').limit(1).get();
}
return utils.responseData(0,"",dbRes.data[0]);
小程序长列表渲染
由于数据库中的存储结构不适合分页,好在总数只有1000条,我们放到前端来处理吧。不过1000条数据一次性setData的话,也还是会很慢。所以第一次只显示100条,滚动到底部再concat下100条的数据。
this.ranklist = this.ranklist.concat(this.orginlist.slice(this.rankpage*100,(this.rankpage+1)*100));
小程序海报生成
整个案子中最麻烦的部分就是海报了,完全使用canvas在前端绘制,并不难,是特别琐碎,要一点点把布局用canvas api画出来。
并且这里遇到了一个不太好办的地方,在微信小程序的canvas里绘制图片,必须先要将图片download到本地,而wx.downloadFile又需要在小程序管理后台配置请求白名单,配置时要求被配置的域名具备国内的备案...
而github头像是这么一个地址
https://avatars2.githubusercontent.com/u/499550?s=140
当我把它配置到白名单里,我获得了这样一个提示
你是不是在坑我!
我上哪儿去给githubusercontent.com
整备案!
我一个人从策划,设计,开发搞了一天一夜,前面开发的时候由于不检测URL白名单,所以问题没有在最早被发现。最后海报实现不了?无法发布?我内心一万头草泥马在狂奔!
必须弄它!
云函数URL化来中转头像
这里我思考了两种做法
-
拿到github推来的数据时,将所有头像转存云存储,替换原url
-
我在自己的云函数中读取头像,并返回图片,无须本地存储,等于利用云函数来做一个中转。
我选择了方案2,就这么干!
方案2要利用云函数的get请求,中转方式如下
云函数url?url=github头像url
云函数中处理http get请求入参
if(event.queryStringParameters){
//github avatar_url fix
var qs = event.queryStringParameters;
var imageRes = await uniCloud.httpclient.request(qs.avatar_url);
let buff = new Buffer(imageRes.data);
let base64data = buff.toString('base64');
return {
mpserverlessComposedResponse: true, // 使用阿里云返回集成响应是需要此字段为true
isBase64Encoded: true,
statusCode: 200,
headers: {
'content-type': 'image/jpeg'
},
body: base64data
}
}
后记
一个人从策划,设计,开发一条龙来完成了这样一个小功能,并且爬了不少坑,思考并解决了一些难点,非常有成就感。
开源
小程序部分的技术实现并不难,所有技术难点都在文章中说明了,大家可以自行实现,这里我主要开源github action和排行榜的json文件,请朋友们点个follow
吧~
我也想冲排名呢! 我也想冲排名呢! 我也想冲排名呢!
设计开发的时间还没有最后整理这篇文章花的时间多,写文章分享不易,还请给与鼓励!