node.js - 使用 Google Docs API 检索 namedRange 中的文本
问题描述
使用带有 Node 的 Google Docs/Drive API,我成功地创建了一个服务,它可以生成“模板”样式的文档,该文档具有命名范围供其他用户写入。我想使用 Google Docs API 来读取在这些范围内输入的文本,但看不到这样做的干净方式。鉴于我有每个范围的开始和结束索引,我认为这将非常简单!不幸的是,我看不到任何内置的方法吗?
目前看来我将不得不请求整个谷歌文档,并且对于我正在观看的每个范围,比较每个节点的开始/结束索引并递归遍历树直到它们匹配。没有更好的方法吗?
干杯
编辑:
下面的 Tanaike 解决方案更简洁,但我已经有了一个适用于我的 Firebase 功能的版本,所以我想我不妨分享一下。此代码检索具有给定 ID 的 Google Doc,并将 namedRanges 的内容作为字符串存储在 Firebase 实时数据库中,通过“BBCode”样式标签保持图像和表格的完整性。下面的相关代码(请注意,我知道每个 namedRange 都在一个表格单元格内,这使得找到它们更容易):
async function StoreResponses(oauth2Client, numSections, documentId, meetingId, revisionId, roomId)
{
var gdocsApi = google.docs({version: 'v1', auth: oauth2Client});
return gdocsApi.documents.get({ "documentId": documentId })
.then((document) => {
var ranges = document.data.namedRanges;
var docContent = document.data.body.content;
var toStore = [];
for(var i = 0; i < numSections; i++)
{
var range = ranges[`zoomsense_section_${i}`].namedRanges[0].ranges[0]
// loop through document contents until we hit the right index
for(var j = 0; j < docContent.length; j++)
{
if(docContent[j].startIndex <= range.startIndex && docContent[j].endIndex >= range.endIndex)
{
// we know that the ranges are inside single table cells
var sectionContents = docContent[j].table.tableRows[0].tableCells[0].content;
toStore.push(readStructuralElementsRecursively(document, sectionContents));
}
}
}
return db.ref(`/data/gdocs/${meetingId}/${roomId}/${documentId}/revisions/${revisionId}/responses`).set(toStore);
})
.catch((exception) => {
console.error(exception)
res.status(500).send(exception);
})
}
// uses https://developers.google.com/docs/api/samples/extract-text
function readStructuralElementsRecursively(document, elements)
{
var text = "";
elements.forEach(element => {
if(element.paragraph)
{
element.paragraph.elements.forEach(elem => {
text += readParagraphElement(document, elem);
});
}
else if(element.table)
{
// The text in table cells are in nested Structural Elements, so this is recursive
text += "[table]"
element.table.tableRows.forEach(row => {
text += "[row]"
row.tableCells.forEach(cell => {
text += `[cell]${readStructuralElementsRecursively(document, cell.content)}[/cell]`;
})
text += "[/row]"
})
text+= "[/table]"
}
});
return text;
}
// handle text and inline content
function readParagraphElement(document, element)
{
if(element.textRun)
{
// standard text
return element.textRun.content;
}
if(element.inlineObjectElement)
{
var objId = element.inlineObjectElement.inlineObjectId;
var imgTag = "\n[img]404[/img]"
try
{
var embeddedObj = document.data.inlineObjects[objId].inlineObjectProperties.embeddedObject;
if(embeddedObj.imageProperties)
{
// this is an image
imgTag = `[img]${embeddedObj.imageProperties.contentUri}[/img]`
}
else if(embeddedObj.embeddedDrawingProperties)
{
// this is a shape/drawing
// can't find any way to meaningfully reference them externally,
// so storing the ID in case we can do it later
imgTag = `[drawing]${objId}[/drawing]`
}
}
catch(exception)
{
console.log(exception)
}
return imgTag;
}
}
解决方案
我相信你的目标如下。
- 您想从 Google 文档的命名范围中检索值。
- 在您的 Google 文档中,已设置命名范围。
- 您想使用 Node.js 来实现这一点。
- 不幸的是,根据您的问题,我无法确认您正在使用的库是否用于使用 Docs API。
为了实现上述目标,我想提出以下解决方法。
问题和解决方法:
不幸的是,在当前阶段,没有直接从 Google Docs API 中的命名范围检索值的方法。我相信将来可能会添加这种方法,因为 Docs API 现在正在增长。因此,作为当前使用 Docs API 的解决方法,需要执行以下流程。
- 使用 Docs API 中的 documents.get 方法检索 Google Document 对象。
- 检索
startIndex
并endIndex
使用命名范围的名称。 startIndex
使用和检索值endIndex
。
您的问题中已经提到了这一点。使用 Google Docs API 时,在当前阶段,需要使用此方法。但是当使用 Google Document 服务时,命名范围的值可以直接通过命名范围的名称和/或 ID 来检索。在这个答案中,我想将此方法作为另一种解决方法。
用法:
请执行以下流程。
1. 新建 Google Apps Script 项目。
Web Apps 的示例脚本是 Google Apps 脚本。所以请创建一个 Google Apps Script 项目。为了使用 Document 服务,在这种情况下,Web Apps 被用作包装器。
如果要直接创建,请访问https://script.new/。在这种情况下,如果您没有登录 Google,则会打开登录屏幕。所以请登录谷歌。至此,Google Apps Script 的脚本编辑器被打开。
2. 准备脚本。
请将以下脚本(Google Apps 脚本)复制并粘贴到脚本编辑器中。此脚本适用于 Web 应用程序。
function doGet(e) {
Object.prototype.getText = function() {return this.getRange().getRangeElements().map(e => e.getElement().asText().getText().slice(e.getStartOffset(), e.getEndOffsetInclusive() + 1))};
const doc = DocumentApp.openById(e.parameter.id);
let res;
if (e.parameter.name) {
const ranges = doc.getNamedRanges(e.parameter.name);
res = ranges.length > 0 ? ranges[0].getText() : [];
} else if (e.parameter.rangeId) {
const range = doc.getNamedRangeById(e.parameter.rangeId.split(".")[1]);
res = range ? range.getText() : [];
} else {
res = [];
}
return ContentService.createTextOutput(JSON.stringify(res));
}
3. 部署 Web 应用程序。
- 在脚本编辑器上,通过“发布”->“部署为 Web 应用程序”打开一个对话框。
- 为“将应用程序执行为:”选择“我” 。
- 这样,脚本作为所有者运行。
- 为“谁有权访问应用程序:”选择“任何人,甚至匿名” 。
- 在这种情况下,不需要请求访问令牌。我认为我建议使用此设置来测试您的目标。
- 当然,您也可以使用访问令牌。届时,请将此设置为“仅我自己”或“任何人”。并请包括访问令牌的范围
https://www.googleapis.com/auth/drive.readonly
和范围。https://www.googleapis.com/auth/drive
这些范围是访问 Web 应用程序所必需的。
- 单击“部署”按钮作为新的“项目版本”。
- 自动打开“需要授权”对话框。
- 单击“查看权限”。
- 选择自己的帐户。
- 在“此应用未验证”中单击“高级”。
- 点击“转到###项目名称###(不安全)”
- 单击“允许”按钮。
- 单击“确定”。
- 复制 Web 应用程序的 URL。就像
https://script.google.com/macros/s/###/exec
。- 当您修改 Google Apps 脚本时,请重新部署为新版本。这样,修改后的脚本就会反映到 Web 应用程序中。请注意这一点。
4. 使用 Web Apps 运行该功能。
您可以使用以下脚本从 Google 电子表格中检索值。
const request = require("request");
const url = "https://script.google.com/macros/s/###/exec"; // Please set the URL of Web Apps.
let qs = {
id: "###", // Please set the Document ID.
name: "###", // Please set the name of named range.
// rangeId: "kix.###", // Please set the ID of named range.
};
let options = {
url: url,
qs: qs,
method: "get",
};
request(options, (err, res, result) => {
if (err) {
console.log(err);
return;
}
console.log(result);
});
- 在这种情况下,结果作为包含值的数组返回。
- 在上述 Web 应用程序中,可以使用命名范围的名称和/或 ID 检索值。当您想使用命名范围的名称时,请使用
let qs = {id: "###", name: "###"};
. 当您想使用命名范围的 ID 时,请使用let qs = {id: "###", rangeId: "kix.###"};
.
笔记:
- 当您修改 Web Apps 的脚本时,请将 Web Apps 重新部署为新版本。这样,最新的脚本就会反映到 Web 应用程序中。请注意这一点。
参考:
推荐阅读
- linux - check_snmp_mem.pl 中的内存使用值是如何得出的?
- c# - 对话框的设计
- r - 在 R 中具有比例的逻辑回归(因变量不是二元的)。R在做什么?
- python - 使用 Python 将 Microsoft Word 文档转换为 PDF
- android - 是否可以在 Activity 类中测试方法?
- javascript - 在jqgrid的隐藏字段中添加formoptions in (colspos and rowspos)
- kubernetes - Kubernetes 在 pod 中获取节点端口映射
- json - 使用 Play JSON 查找具有动态键的 JSON 元素的路径
- jquery - 在 jquery 中加载页面后,键盘选项卡按钮应该可以工作
- java - Android内部网络回调事件外部变量未找到