连接到 API:分析反馈情绪

编码级别:中级
时长:20 分钟
项目类型:使用自定义菜单的自动化

目标

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

关于此解决方案

您可以大规模分析各种文本数据,比如用户以自由作答形式提供的反馈。为了在 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. 现在,您可以跳过添加镜重,然后点击保存并继续。今后,如果您创建的应用要在 Google Workspace 组织之外使用,则必须将用户类型更改为外部,然后添加应用所需的授权范围。

  5. 查看应用注册摘要。如需进行更改,请点击修改。如果应用注册未出现任何问题,请点击返回信息中心

获取 Google Cloud Natural Language API 的 API 密钥

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

    转到“凭据”页面

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

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

设置脚本

创建 Apps 脚本项目

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

添加文本数据

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

运行脚本

  1. 在电子表格的顶部,依次点击情感分析工具 > 标记实体和情感。您可能需要刷新页面,此自定义菜单才会显示。
  2. 根据提示为脚本授权。如果 OAuth 意见征求界面显示此应用未经验证警告,请依次选择高级 > 前往 {Project Name}(不安全)以继续操作。

  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 开发者专家提供帮助。

后续步骤