【问题标题】:Retrieve text in namedRange with Google Docs API使用 Google Docs API 检索 namedRange 中的文本
【发布时间】:2020-07-08 14:30:36
【问题描述】:

将 Google Docs/Drive API 与 Node 一起使用,我成功地制作了一项服务,该服务可生成“模板”样式的文档,该文档具有命名范围供其他用户写入。我想使用 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;
    }
}

【问题讨论】:

  • 我提出了一种解决方法来实现您的目标作为答案。你能确认一下吗?如果这不是您期望的方向,我深表歉意。

标签: node.js google-apps-script google-docs-api named-ranges


【解决方案1】:

我相信你的目标如下。

  • 您想从 Google 文档的指定范围中检索值。
  • 在您的 Google 文档中,已设置命名范围。
  • 您希望使用 Node.js 实现此目的。
    • 很遗憾,根据您的问题,我无法确认您正在使用的库是否用于使用 Docs API。

为了实现上述目的,我想提出以下解决方法。

问题和解决方法:

不幸的是,在当前阶段,没有直接从 Google Docs API 中的命名范围检索值的方法。我相信将来可能会添加这种方法,因为 Docs API 现在正在增长。所以作为目前使用Docs API的解决方法,需要做以下流程。

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

您的问题中已经提到了这一点。使用 Google Docs API 时,在当前阶段,需要使用此方法。但是当使用 Google Document 服务时,可以通过命名范围的名称和/或 ID 直接检索命名范围的值。在这个答案中,我想将此方法作为另一种解决方法。

用法:

请执行以下流程。

1。创建 Google Apps 脚本的新项目。

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 应用程序。

  1. 在脚本编辑器上,通过“发布”->“部署为 Web 应用”打开一个对话框。
  2. “执行应用程序为:”选择“我”
    • 由此,脚本以所有者身份运行。
  3. “谁有权访问应用程序:”选择“任何人,甚至匿名”
    • 在这种情况下,不需要请求访问令牌。我认为我建议使用此设置来测试您的目标。
    • 当然,您也可以使用访问令牌。届时,请将其设置为“仅限我自己”“任何人”。并且请在访问令牌中包含https://www.googleapis.com/auth/drive.readonlyhttps://www.googleapis.com/auth/drive 的范围。这些范围是访问 Web 应用所必需的。
  4. 单击“部署”按钮作为新的“项目版本”。
  5. 自动打开“需要授权”对话框。
    1. 点击“查看权限”。
    2. 选择自己的帐户。
    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 应用程序中,可以使用命名范围的名称和/或 ID 检索值。当您想使用命名范围的名称时,请使用let qs = {id: "###", name: "###"};。当您想使用命名范围的ID时,请使用let qs = {id: "###", rangeId: "kix.###"};

注意:

  • 当您修改 Web Apps 的脚本时,请将 Web Apps 重新部署为新版本。这样,最新的脚本就会反映到 Web 应用程序中。请注意这一点。

参考资料:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-06-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多