使用 Google 文档 API 在 namedRange 中检索文本
Retrieve text in namedRange with Google Docs API
在 Node 中使用 Google Docs/Drive APIs,我已经成功地创建了一个服务,它生成 'template' 风格的文档,这些文档具有 namedRanges 供其他用户编写进入。我想使用 Google 文档 API 来读取在这些范围内输入的文本,但看不到一种干净的方法。鉴于我有每个范围的开始和结束索引,我认为这会非常简单!不幸的是,我看不到任何内置的方法?
目前看来我将不得不请求整个 google 文档,并且对于我正在观察的每个范围,比较每个节点的 start/end 索引并向下递归遍历树直到它们比赛。没有更好的方法吗?
干杯
编辑:
下面 Tanaike 的解决方案更清晰,但我已经有一个适用于我的 Firebase 函数的版本,所以我想我不妨分享一下。此代码检索具有给定 ID 的 Google 文档,并将 namedRanges 的内容作为字符串存储在 Firebase 实时数据库中,通过“BBCode”样式标签保持图像和 table 完整无缺。下面的相关代码(请注意,我知道每个 namedRange 都在 table 单元格内,这使得找到它们更容易):
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 文档 API 中没有直接从命名范围中检索值的方法。我相信将来可能会添加这种方法,因为 Docs API 现在正在增长。因此,作为当前使用 Docs API 的解决方法,需要执行以下流程。
- 使用文档documents.get的方法检索Google文档对象API.
- 使用命名范围的名称检索
startIndex
和 endIndex
。
- 使用
startIndex
和 endIndex
检索值。
这在你的问题中已经提到了。当使用Google Docs API时,在当前阶段需要使用此方法。但是当使用 Google 文档服务时,命名范围的值可以直接通过名称 and/or 命名范围的 ID 来检索。在这个答案中,我想提出这种方法作为另一种解决方法。
用法:
请执行以下流程。
1。创建 Google Apps 脚本的新项目。
Web Apps 的示例脚本是 Google Apps 脚本。所以请创建一个 Google Apps Script 的项目。为了使用文档服务,在这种情况下,Web Apps 被用作包装器。
如果要直接创建,请访问https://script.new/。在这种情况下,如果您未登录 Google,则会打开登录屏幕。所以请登录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 应用程序。
- 在脚本编辑器上,通过“发布”->“部署为网络应用程序”打开一个对话框。
- Select “我” for “执行应用程序:”。
- 至此,脚本运行作为所有者。
- Select “任何人,甚至是匿名的” for “有权访问该应用程序的人:”。
- 在这种情况下,请求不需要访问令牌。我想我推荐这个设置来测试你的目标。
- 当然你也可以使用access token。届时,请设置为“仅限我自己”或“任何人”。并且请将
https://www.googleapis.com/auth/drive.readonly
和 https://www.googleapis.com/auth/drive
的范围包含在访问令牌中。这些范围是访问 Web 应用程序所必需的。
- 点击“部署”按钮作为新的“项目版本”。
- 自动打开“需要授权”的对话框。
- 点击“查看权限”。
- Select自己的账号。
- 点击“此应用未验证”处的“高级”。
- 点击“转到###项目名称###(不安全)”
- 单击“允许”按钮。
- 点击“确定”。
- 复制 Web 应用程序的 URL。就像
https://script.google.com/macros/s/###/exec
。
- 当您修改 Google Apps 脚本时,请重新部署为新版本。这样,修改后的脚本就会反映到 Web 应用程序中。请注意这一点。
4。 运行 使用 Web 应用程序的功能。
您可以使用以下脚本从 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 应用程序中,可以使用命名范围的名称 and/or ID 来检索值。当您想使用命名范围的名称时,请使用
let qs = {id: "###", name: "###"};
。当要使用命名范围的ID时,请使用let qs = {id: "###", rangeId: "kix.###"};
.
注:
- 当您修改了Web Apps的脚本后,请将Web Apps重新部署为新版本。由此,最新的脚本被反映到Web Apps。请注意这一点。
参考文献:
在 Node 中使用 Google Docs/Drive APIs,我已经成功地创建了一个服务,它生成 'template' 风格的文档,这些文档具有 namedRanges 供其他用户编写进入。我想使用 Google 文档 API 来读取在这些范围内输入的文本,但看不到一种干净的方法。鉴于我有每个范围的开始和结束索引,我认为这会非常简单!不幸的是,我看不到任何内置的方法?
目前看来我将不得不请求整个 google 文档,并且对于我正在观察的每个范围,比较每个节点的 start/end 索引并向下递归遍历树直到它们比赛。没有更好的方法吗?
干杯
编辑:
下面 Tanaike 的解决方案更清晰,但我已经有一个适用于我的 Firebase 函数的版本,所以我想我不妨分享一下。此代码检索具有给定 ID 的 Google 文档,并将 namedRanges 的内容作为字符串存储在 Firebase 实时数据库中,通过“BBCode”样式标签保持图像和 table 完整无缺。下面的相关代码(请注意,我知道每个 namedRange 都在 table 单元格内,这使得找到它们更容易):
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 文档 API 中没有直接从命名范围中检索值的方法。我相信将来可能会添加这种方法,因为 Docs API 现在正在增长。因此,作为当前使用 Docs API 的解决方法,需要执行以下流程。
- 使用文档documents.get的方法检索Google文档对象API.
- 使用命名范围的名称检索
startIndex
和endIndex
。 - 使用
startIndex
和endIndex
检索值。
这在你的问题中已经提到了。当使用Google Docs API时,在当前阶段需要使用此方法。但是当使用 Google 文档服务时,命名范围的值可以直接通过名称 and/or 命名范围的 ID 来检索。在这个答案中,我想提出这种方法作为另一种解决方法。
用法:
请执行以下流程。
1。创建 Google Apps 脚本的新项目。
Web Apps 的示例脚本是 Google Apps 脚本。所以请创建一个 Google Apps Script 的项目。为了使用文档服务,在这种情况下,Web Apps 被用作包装器。
如果要直接创建,请访问https://script.new/。在这种情况下,如果您未登录 Google,则会打开登录屏幕。所以请登录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 应用程序。
- 在脚本编辑器上,通过“发布”->“部署为网络应用程序”打开一个对话框。
- Select “我” for “执行应用程序:”。
- 至此,脚本运行作为所有者。
- Select “任何人,甚至是匿名的” for “有权访问该应用程序的人:”。
- 在这种情况下,请求不需要访问令牌。我想我推荐这个设置来测试你的目标。
- 当然你也可以使用access token。届时,请设置为“仅限我自己”或“任何人”。并且请将
https://www.googleapis.com/auth/drive.readonly
和https://www.googleapis.com/auth/drive
的范围包含在访问令牌中。这些范围是访问 Web 应用程序所必需的。
- 点击“部署”按钮作为新的“项目版本”。
- 自动打开“需要授权”的对话框。
- 点击“查看权限”。
- Select自己的账号。
- 点击“此应用未验证”处的“高级”。
- 点击“转到###项目名称###(不安全)”
- 单击“允许”按钮。
- 点击“确定”。
- 复制 Web 应用程序的 URL。就像
https://script.google.com/macros/s/###/exec
。- 当您修改 Google Apps 脚本时,请重新部署为新版本。这样,修改后的脚本就会反映到 Web 应用程序中。请注意这一点。
4。 运行 使用 Web 应用程序的功能。
您可以使用以下脚本从 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 应用程序中,可以使用命名范围的名称 and/or ID 来检索值。当您想使用命名范围的名称时,请使用
let qs = {id: "###", name: "###"};
。当要使用命名范围的ID时,请使用let qs = {id: "###", rangeId: "kix.###"};
.
注:
- 当您修改了Web Apps的脚本后,请将Web Apps重新部署为新版本。由此,最新的脚本被反映到Web Apps。请注意这一点。