使用 HTTP API 从 WPS 在线表格中获取数据

之前的博文中,我提到过本站的友情链接是通过 Vika 维格表管理的,维格表的一大特色是支持通过 API 增、删、改、查表格中的数据,这直接让维格表化身为一个简单的数据库,可玩性大大提升,通过它维护友链数据,并通过自己编写的脚本生成友链页面就是个例子。

然而最近我放弃了维格表,改用 WPS 在线表格,原因有四点:

  1. Vika 页面经常有奇奇怪怪的 bug,比如拖拽改变数据顺序时,数据会乱跑;
  2. 性能优化不行,在我仍在服役的那台十年前的笔记本上,用 WPS 还行,用 Vika 一定会卡;
  3. 每次打开都有一个开通会员的流氓弹窗,这个弹窗的 × 号是个假的,第一次点击会跳转到开通会员的页面,第二次点击才会关闭弹窗,怪难受的;

关于第三点的流氓弹窗,悄悄告诉你一个解决办法——

1
2
// 打开 F12 - Console,执行
localStorage.setItem("LAST_PAYMENT_REMINDER_TIME", "9999999999999");
  1. Vika 免费空间只支持 20,000 行数据、上传 30 个文件,我已经用了 50% 了,该给数据寻找新家了。

将 Vika 的所有数据迁移至 WPS 在线表格后,最大的问题就是该如何通过 API 查询数据。Vika 中有方便的 @vikadata/vika SDK,WPS 其实也有方便的 HTTP 接口,通过查阅 金山文档开放平台,我发现在线表格可以通过遍历记录接口获取数据,然而事情真的有这么简单吗?

问题一:想要调用金山文档开放平台的接口,需要认证成为 WPS 开发者,认证需要营业执照,且 不支持个人认证,这不是为难我这个不准开公司的银行小打工人吗?

问题二:即使认证成功,调用接口也需要 access token,一个 token 的有效期仅有一天,刷新 token 需要手动 访问授权页 获取,极其不便。

一番探索后,我便放弃了这个方案。因为我发现了一个更方便的路子:AirScript 脚本

根据官方介绍,AirScript 是一个基于 JavaScript 的表格应用开发工具,它支持编程完成一些表格操作,下面介绍如何利用 AirScript,实现本站的友链页面自动生成。

登录金山文档,创建一个 智能表格

在智能表格中,新建一个 数据表

在数据表中,根据需要增加站名、签名、状态、首页、友链页、RSS、头像、备注等字段,然后录入所有友链数据。

点击 Ribbon 栏中的高级功能 - 高级开发 - AirScript 脚本编辑器。

文档共享脚本 中新建一个脚本。

不要放在 我的脚本 中,因为不支持 webhook 调用。

填入脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 将 ‘友链’ 改为实际的工作表名称
const sheet = Application.Sheets.Item('友链')

// 分页获取全表数据,如果不分页,一次最多只能读取 100 条数据
function fetchAllRecords() {
const view = sheet.Selection.GetActiveView()
let all = []
let offset = null
while (all.length === 0 || offset) {
let records = sheet.Record.GetRecords({
ViewId: view.viewId,
Offset: offset,
})
offset = records.offset
all = all.concat(records.records)
}
return all
}

// return 的数据会通过接口的响应体返回
return fetchAllRecords()

按 Ctrl + S 保存脚本,然后点击工具栏上的 “脚本令牌” 按钮,生成一个脚本令牌,复制这个令牌保存(以后将无法再次查看),令牌有效期是半年,但放心,令牌可以随时延期,延期后令牌不变。

点击脚本名称旁边的菜单,点击 “复制脚本 webhook” 并保存。

接下来只需 POST 这个 webhook 地址,在请求头中带上 AirScript-Token: 脚本令牌,即可获取到 JSON 格式的表格数据。

接下来就可以搭配本地脚本实现自动生成友链页面了!在博客源码目录新建一个 friends-gen.js,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
const fs = require("fs");
const path = require("path");

// TODO: 修改这里的友情链接模板方法,以适配你的友链页面样式
function template({ 站名, 首页, RSS, 头像, 签名, 状态, 备注 }) {
if (!站名 || 状态 === "已清理") return "";
站名 = 站名 ? 站名.replace(/(’|‘)/g, "'") : "";
签名 = 签名 ? 签名.replace(/(’|‘)/g, "'") : "";
const showAvatar = 头像 && 状态 === "正常";
const avatar = showAvatar ? ` style="background-image: url('${头像}')"` : "";
const firstWord = showAvatar ? "" : 站名[0].toUpperCase();
const rss =
RSS && RSS !== "无" && 状态 === "正常"
? `&nbsp;<a class="has-text-grey" target="_blank" href="${RSS}"><i class="fas fa-rss" title="支持 RSS"></i></a>`
: "";
const tag = {
正常: "",
无法访问: '&nbsp;<span class="tag is-warning">无法访问</span>',
单向友链: '&nbsp;<span class="tag is-warning">单向友链</span>',
已清理: '&nbsp;<span class="tag is-Danger">已清理</span>',
}[状态];
const popList = [];
if (签名) {
popList.push(`签名: ${签名}`);
}
if (备注 && 状态 !== "正常") {
popList.push(`备注: ${备注}`);
}
const pop =
popList.length > 0
? `
<div class="friend-pop"><div>${popList.join("<br />")}</div></div>`
: "";
const link =
状态 === "正常"
? `<a target="_blank" href="${首页}">${首页.replace(
/^https?:\/\//,
""
)}</a>`
: 首页.replace(/^https?:\/\//, "");
return ` <div class="friend">
<div class="friend-avatar"${avatar}>${firstWord}</div>
<div class="friend-detail">
<div>${站名}${tag}${rss}</div>
<div>${link}</div>
</div>${pop}
</div>
`;
}

async function main() {
const res = await fetch(
// TODO: 替换为你的脚本 webhook
"https://www.kdocs.cn/api/v3/ide/file/******/script/******/sync_task",
{
method: "POST",
headers: {
"Content-Type": "application/json",
// TODO: 替换为你的脚本令牌
"AirScript-Token": "******",
},
body: JSON.stringify({
Context: {
argv: {},
},
}),
}
);
const jsonRes = await res.json();
if (jsonRes && jsonRes.status === "finished") {
const records = jsonRes.data.result;
let result = `
{% raw %}
<div class="friends">
`;
let normalCompleted = false;
for (const record of records) {
if (!normalCompleted && record.fields.状态 !== "正常") {
result += ` <div class="friend friend-empty"></div>
<div class="friend friend-empty"></div>
<div class="friend friend-empty"></div>
</div>
{% endraw %}

----

异常友链 | 如存在异议请在评论区留言

{% raw %}
<div class="friends">
`;
normalCompleted = true;
}
result += template(record.fields);
}
result += ` <div class="friend friend-empty"></div>
<div class="friend friend-empty"></div>
<div class="friend friend-empty"></div>
</div>
{% endraw %}
`;
// TODO: 替换为你的友情链接 md 文件路径
const friendsMd = path.resolve(__dirname, "./source/friends/index.md");
const friendsMdContent = fs.readFileSync(friendsMd, { encoding: "utf-8" });
const startStr = "<!-- auto_generation_start -->";
const endStr = "<!-- auto_generation_end -->";
const startPos = friendsMdContent.indexOf(startStr) + startStr.length;
const endPos = friendsMdContent.indexOf(endStr, startPos);
const newContent =
friendsMdContent.slice(0, startPos) +
result +
friendsMdContent.slice(endPos);
fs.writeFileSync(friendsMd, newContent, { encoding: "utf-8" });
}
}

main();

请按照脚本中 // TODO: 注释标注的要求自己修改脚本。

打开友情链接的 md 文件,在需要自动生成的位置增加起始和结束标记。

1
2
<!-- auto_generation_start -->
<!-- auto_generation_end -->

执行脚本 node friend-gen.js,即可测试生成效果了!

通过这种方式完成友链自动化后,你还可以点击友链表格右上角的创建应用 - 表单,生成表单供访客自助提交友链申请,真方便呀!

使用 HTTP API 从 WPS 在线表格中获取数据

https://www.imaegoo.com/2024/http-get-data-from-wps/

作者

iMaeGoo

发布于

2024-08-24

更新于

2024-08-26

许可协议

CC BY 4.0

评论

微信二维码