连接到 API:分析反馈情绪

编码级别:中级
时长:20 分钟
项目类型:通过自定义菜单实现自动化

目标

  • 了解解决方案的用途。
  • 了解 Apps 脚本服务在此解决方案中执行的操作。
  • 设置环境。
  • 设置脚本。
  • 运行脚本。

关于此解决方案

您可以大规模分析文本数据,例如开放式反馈。如需在 Google 表格中执行实体和情感分析,此解决方案使用 UrlFetch Service 连接到 Google Cloud Natural Language API

情感分析工作原理示意图

运作方式

该脚本会收集电子表格中的文本,并连接到 Google Cloud Natural Language API 以分析字符串中存在的实体和情感。数据透视表汇总了所有文本数据行中提到的每个实体的平均情感得分。

Apps 脚本服务

此解决方案使用以下服务:

  • 电子表格服务 - 将文本数据发送到 Google Cloud Natural Language API,并在分析完每行情感后将每一行标记为“完成”。
  • UrlFetch 服务 - 连接到 Google Cloud Natural Language API 以对文本执行实体和情感分析。

前提条件

如需使用此示例,您需要满足以下前提条件:

  • Google 帐号(Google Workspace 帐号可能需要管理员批准)。
  • 一个能够访问互联网的网络浏览器。

  • 具有关联的结算帐号的 Google Cloud 项目。请参阅为项目启用结算功能

设置您的环境

在 Google Cloud 控制台中打开您的 Cloud 项目

打开您打算用于此示例的 Cloud 项目(如果尚未打开):

  1. 在 Google Cloud 控制台中,前往选择项目页面。

    选择 Cloud 项目

  2. 选择您要使用的 Google Cloud 项目。或者,点击创建项目,然后按照屏幕上的说明操作。如果您创建了 Google Cloud 项目,则可能需要为该项目启用结算功能

启用 Google Cloud Natural Language API

此解决方案连接到 Google Cloud Natural Language API。在使用 Google API 之前,您需要在 Google Cloud 项目中启用它们。您可以在单个 Google Cloud 项目中启用一个或多个 API。

  • 在您的 Cloud 项目中,启用 Google Cloud Natural Language API。

    启用 API

此解决方案需要一个 Cloud 项目,其中已配置权限请求页面。配置 OAuth 同意屏幕可定义 Google 向用户显示的内容并注册您的应用,以便您稍后发布应用。

  1. 在 Google Cloud 控制台中,依次点击“菜单”图标 > API 和服务 > OAuth 同意屏幕

    转到 OAuth 同意屏幕

  2. 对于用户类型,选择内部,然后点击创建
  3. 填写应用注册表单,然后点击保存并继续
  4. 目前,您可以跳过添加范围的步骤,点击 Save and Continue(保存并继续)。 将来,当您创建要在 Google Workspace 组织外部使用的应用时,必须将用户类型更改为外部,然后添加您的应用所需的授权范围。

  5. 查看您的应用注册摘要。如要进行更改,请点击修改。如果应用注册看起来正常,请点击 Back to Dashboard

获取 Google Cloud Natural Language API 的 API 密钥

  1. 前往 Google Cloud 控制台。 确保启用了结算功能的项目处于打开状态。
  2. 在 Google Cloud 控制台中,依次点击“菜单”图标 > API 和服务 > 凭据

    进入“凭据”页面

  3. 依次点击创建凭据 > API 密钥

  4. 记下您的 API 密钥,以便在后续步骤中使用。

设置脚本

创建 Apps 脚本项目

  1. 点击下面的按钮,创建获取反馈的情感分析示例电子表格的副本。此解决方案的 Apps 脚本项目已附加到该电子表格中。
    复制
  2. 点击扩展程序 > Apps 脚本
  3. 使用您的 API 密钥更新脚本文件中的以下变量:
    const myApiKey = 'YOUR_API_KEY'; // Replace with your API key.
  4. 点击“保存”“保存”图标

添加文本数据

  1. 返回到电子表格。
  2. 将文本数据添加到 id 列和 comments 列。您可以使用 Kaggle 中的度假房源示例,也可以使用自己的数据。您可以根据需要添加更多列,但要成功运行,脚本的 idcomments 列中必须包含数据。

运行脚本

  1. 在电子表格的顶部,点击情感工具 > 标记实体和情感。您可能需要刷新页面才能看到此自定义菜单。
  2. 出现提示时,授权脚本。如果 OAuth 同意屏幕显示 This app is not verified(此应用未经验证)警告,请依次选择 Advanced > Go to {Project Name} (unsafe),继续操作。

  3. 依次点击情感工具 > 标记实体和情感

  4. 脚本运行完毕后,切换到数据透视表工作表以查看结果。

查看代码

如需查看此解决方案的 Apps 脚本代码,请点击下面的查看源代码

查看源代码

Code.gs

solutions/automations/feedback-sentiment-analysis/code.js
// To learn how to use this script, refer to the documentation:
// https://developers.google.com/apps-script/samples/automations/feedback-sentiment-analysis

/*
Copyright 2022 Google LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Sets API key for accessing Cloud Natural Language API.
const myApiKey = 'YOUR_API_KEY'; // Replace with your API key.

// Matches column names in Review Data sheet to variables.
let COLUMN_NAME = {
  COMMENTS: 'comments',
  ENTITY: 'entity_sentiment',
  ID: 'id'
};

/**
 * Creates a Demo menu in Google Spreadsheets.
 */
function onOpen() {
  SpreadsheetApp.getUi()
    .createMenu('Sentiment Tools')
    .addItem('Mark entities and sentiment', 'markEntitySentiment')
    .addToUi();
};

/**
* Analyzes entities and sentiment for each comment in  
* Review Data sheet and copies results into the 
* Entity Sentiment Data sheet.
*/
function markEntitySentiment() {
  // Sets variables for "Review Data" sheet
  let ss = SpreadsheetApp.getActiveSpreadsheet();
  let dataSheet = ss.getSheetByName('Review Data');
  let rows = dataSheet.getDataRange();
  let numRows = rows.getNumRows();
  let values = rows.getValues();
  let headerRow = values[0];

  // Checks to see if "Entity Sentiment Data" sheet is present, and
  // if not, creates a new sheet and sets the header row.
  let entitySheet = ss.getSheetByName('Entity Sentiment Data');
  if (entitySheet == null) {
   ss.insertSheet('Entity Sentiment Data');
   let entitySheet = ss.getSheetByName('Entity Sentiment Data');
   let esHeaderRange = entitySheet.getRange(1,1,1,6);
   let esHeader = [['Review ID','Entity','Salience','Sentiment Score',
                    'Sentiment Magnitude','Number of mentions']];
   esHeaderRange.setValues(esHeader);
  };

  // Finds the column index for comments, language_detected, 
  // and comments_english columns.
  let textColumnIdx = headerRow.indexOf(COLUMN_NAME.COMMENTS);
  let entityColumnIdx = headerRow.indexOf(COLUMN_NAME.ENTITY);
  let idColumnIdx = headerRow.indexOf(COLUMN_NAME.ID);
  if (entityColumnIdx == -1) {
    Browser.msgBox("Error: Could not find the column named " + COLUMN_NAME.ENTITY + 
                   ". Please create an empty column with header \"entity_sentiment\" on the Review Data tab.");
    return; // bail
  };

  ss.toast("Analyzing entities and sentiment...");
  for (let i = 0; i < numRows; ++i) {
    let value = values[i];
    let commentEnCellVal = value[textColumnIdx];
    let entityCellVal = value[entityColumnIdx];
    let reviewId = value[idColumnIdx];

    // Calls retrieveEntitySentiment function for each row that has a comment 
    // and also an empty entity_sentiment cell value.
    if(commentEnCellVal && !entityCellVal) {
        let nlData = retrieveEntitySentiment(commentEnCellVal);
        // Pastes each entity and sentiment score into Entity Sentiment Data sheet.
        let newValues = []
        for (let entity in nlData.entities) {
          entity = nlData.entities [entity];
          let row = [reviewId, entity.name, entity.salience, entity.sentiment.score, 
                     entity.sentiment.magnitude, entity.mentions.length
                    ];
          newValues.push(row);
        }
      if(newValues.length) {
        entitySheet.getRange(entitySheet.getLastRow() + 1, 1, newValues.length, newValues[0].length).setValues(newValues);
      }
        // Pastes "complete" into entity_sentiment column to denote completion of NL API call.
        dataSheet.getRange(i+1, entityColumnIdx+1).setValue("complete");
     }
   }
};

/**
 * Calls the Cloud Natural Language API with a string of text to analyze
 * entities and sentiment present in the string.
 * @param {String} the string for entity sentiment analysis
 * @return {Object} the entities and related sentiment present in the string
 */
function retrieveEntitySentiment (line) {
  let apiKey = myApiKey;
  let apiEndpoint = 'https://language.googleapis.com/v1/documents:analyzeEntitySentiment?key=' + apiKey;
  // Creates a JSON request, with text string, language, type and encoding
  let nlData = {
    document: {
      language: 'en-us',
      type: 'PLAIN_TEXT',
      content: line
    },
    encodingType: 'UTF8'
  };
  // Packages all of the options and the data together for the API call.
  let nlOptions = {
    method : 'post',
    contentType: 'application/json',  
    payload : JSON.stringify(nlData)
  };
  // Makes the API call.
  let response = UrlFetchApp.fetch(apiEndpoint, nlOptions);
  return JSON.parse(response);
};

贡献者

此示例由 Google 在 Google 开发者专家的帮助下进行维护。

后续步骤