使用 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 的解决方法,需要执行以下流程。

  1. 使用文档documents.get的方法检索Google文档对象API.
  2. 使用命名范围的名称检索 startIndexendIndex
  3. 使用 startIndexendIndex 检索值。

这在你的问题中已经提到了。当使用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 应用程序。

  1. 在脚本编辑器上,通过“发布”->“部署为网络应用程序”打开一个对话框。
  2. Select “我” for “执行应用程序:”
    • 至此,脚本运行作为所有者。
  3. Select “任何人,甚至是匿名的” for “有权访问该应用程序的人:”
    • 在这种情况下,请求不需要访问令牌。我想我推荐这个设置来测试你的目标。
    • 当然你也可以使用access token。届时,请设置为“仅限我自己”“任何人”。并且请将 https://www.googleapis.com/auth/drive.readonlyhttps://www.googleapis.com/auth/drive 的范围包含在访问令牌中。这些范围是访问 Web 应用程序所必需的。
  4. 点击“部署”按钮作为新的“项目版本”。
  5. 自动打开“需要授权”的对话框。
    1. 点击“查看权限”。
    2. Select自己的账号。
    3. 点击“此应用未验证”处的“高级”。
    4. 点击“转到###项目名称###(不安全)”
    5. 单击“允许”按钮。
  6. 点击“确定”。
  7. 复制 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。请注意这一点。

参考文献: