Google Sheet + Apps Script + Glide
背 GRE 单词的时候弄的一套自动化流程,可以批量查词修改 Spreadsheet,配合 GlideApp 生成 PWA 在手机上用,效果可以参考这里。
1) 导入 Apps Script
这个脚本基本上就两个功能,一个是触发器,负责在修改文档的时候更新那一行的内容,第二个是一些数值函数,可以直接在 Sheet 的 f(x) 框里面用。所有查询的词都做了 lowercase 和 cache,毕竟 UrlFetchApp
有配额一天只能跑上几千次,要省着点用,而且如果用了 f(x) 表达式的话,每次打开页面都请求爱词霸也挺拖累别人服务器的。
appsscript.json
{
"timeZone": "Asia/Hong_Kong",
"dependencies": {
},
"webapp": {
"access": "ANYONE",
"executeAs": "USER_ACCESSING"
},
"exceptionLogging": "STACKDRIVER",
"oauthScopes": ["https://www.googleapis.com/auth/documents", "https://www.googleapis.com/auth/script.external_request", "https://www.googleapis.com/auth/spreadsheets"]
}
code.gs
function onOpen() {
var ui = SpreadsheetApp.getUi();
// Or DocumentApp or FormApp.
ui.createMenu('Words')
.addItem('Reset Cache', 'ResetCache')
.addToUi();
}
function onEditInstalledTrigger(e) {
var range = e.range;
var sheet = SpreadsheetApp.getActiveSheet();
Logger.log("row = " + range.getRow() + " column = " + range.getColumn);
Logger.log("row Number = " + range.getNumRows() + " column Number = " + range.getNumColumns());
for (var i = range.getRow(); i <= range.getRow() + range.getNumRows() - 1; i++) {
for (var j = range.getColumn(); j <= range.getColumn() + range.getNumColumns() - 1; j++)
{
if (j == 1)
{
var currentValue = sheet.getRange(i,j).getValue();
var wordLowCase = GetWordLowerCase(currentValue);
var empty = IsEmpty(wordLowCase);
var cache = IsCached(wordLowCase);
Logger.log("row i = " + i + " column j = " + i + " word = " + wordLowCase);
var attribute = {
cache:cache,
value:wordLowCase,
empty:empty
}
sheet.getRange(i,j).setNote(empty ? "" : JSON.stringify(attribute));
sheet.getRange(i,j+1).setValue(empty ? "" : "LOADING");
sheet.getRange(i,j+2).setValue(empty ? "" : "LOADING");
sheet.getRange(i,j+3).setValue(empty ? "" : "LOADING");
sheet.getRange(i,j+4).setValue(empty ? "" : "LOADING");
sheet.getRange(i,j+5).setValue(empty ? "" : "LOADING");
var wordJsonObject = empty ? {} : GetWord(wordLowCase);
attribute.json = wordJsonObject;
sheet.getRange(i,j).setNote(empty ? "" : JSON.stringify(attribute));
Logger.log(JSON.stringify(attribute));
if (wordJsonObject.status == 0)
{
var wordMeaning = empty ? "" : wordJsonObject.content.word_mean.join("\r\n");
sheet.getRange(i,j+1).setValue(wordMeaning);
var worldPronunciationUS = empty ? "" : "/" + wordJsonObject.content.ph_am + "/";
sheet.getRange(i,j+2).setValue(worldPronunciationUS);
var worldPronunciationURLUS = empty ? "" : wordJsonObject.content.ph_am_mp3;
sheet.getRange(i,j+3).setValue(worldPronunciationURLUS);
var cachedString = empty ? "" : (cache ? "YES" : "NO");
sheet.getRange(i,j+4).setValue(cachedString);
}
var updateTime = empty ? "" : new Date().toISOString();
sheet.getRange(i,j+5).setValue(updateTime);
}
}
}
}
function ResetCache() {
var cache = CacheService.getScriptCache();
var selection = SpreadsheetApp.getActiveSheet();
var data = selection.getSelection().getActiveRange().getValues()
for (var i = 0; i < data.length; i++) {
var wordLowCase = GetWordLowerCase(data[i][0]);
Logger.log('Cache remove: ' + wordLowCase);
cache.remove(wordLowCase);
}
}
function GetWordLowerCase(word)
{
return String(word).toLowerCase();
}
function GetWordUrl(word)
{
var wordLowCase = GetWordLowerCase(word);
var url = "http://fy.iciba.com/ajax.php?a=fy&f=auto&t=zh&w=".concat(encodeURIComponent(wordLowCase));
return url;
}
function IsCached(word)
{
if (IsEmpty(word))
{
return false;
}
var wordLowCase = GetWordLowerCase(word);
var cache = CacheService.getScriptCache();
var cached = cache.get(wordLowCase);
return cached != null;
}
function IsEmpty(str) {
return (!str || 0 === str.length);
}
/**
* Get Word JSON Object.
*
* @param {word} the word.
* @return JSON returned from iciba.
* @customfunction
*/
function GetWord(word) {
if (IsEmpty(word))
{
Logger.log("word == null");
return {};
}
var wordLowCase = GetWordLowerCase(word);
var cache = CacheService.getScriptCache();
var cached = cache.get(wordLowCase);
if (cached != null) {
return JSON.parse(cached);
}
var url = GetWordUrl(wordLowCase);
var jsonData = UrlFetchApp.fetch(url);
var jsonContent = jsonData.getContentText();
cache.put(wordLowCase, jsonContent, 604800); // cache for random minutes
var object = JSON.parse(jsonContent);
return object;
}
/**
* @customfunction
*/
function GetWordMeaning(word)
{
var object = GetWord(word);
return object.content.word_mean.join("\r\n");
}
/**
* @customfunction
*/
function GetWordPronunciationUS(word)
{
var object = GetWord(word);
return object.content.ph_am;
}
/**
* @customfunction
*/
function GetWordPronunciationUSURL(word)
{
var object = GetWord(word);
return object.content.ph_am_mp3;
}
/**
* @customfunction
*/
function GetWordJSON(word)
{
var object = GetWord(word);
return JSON.stringify(object);
}
然后部署下,允许下文件访问
2) 安装 Trigger
表格 - 工具 - 脚本触发器 - 修改 - 当前项目触发器 - 添加:
- 选择要运行的功能 OnEditInstalledTrigger
- 选择活动来源 基于电子表格
- 选择活动来源 编辑时
3) 输入数据
第一行最好 freeze 住,然后在第二行开始输入英文,Trigger 会自动补全后面几列
4) 同步到 Glide 里
去 Glide 里新建一个项目绑定这个表格。
Glide 比较好的地方在于,他们的 Detail Page 可以设定 Audio 组件绑定 URL,刚好可以对应爱词霸 API 里的音标地址,基本配置完可以当一个自定义单词本来用了。