連結至 API:分析意見回饋情緒

程式設計程度:中級
時間:20 分鐘
專案類型:使用自訂選單的自動化動作

目標

  • 瞭解解決方案的功能。
  • 瞭解 Apps Script 服務在解決方案中的作用。
  • 設定環境。
  • 設定指令碼。
  • 執行指令碼。

認識這項解決方案

您可以大量分析文字資料 (例如開放式意見回饋)。為了在 Google 試算表中執行實體和情緒分析,這個解決方案會使用 UrlFetch 服務連線至 Google Cloud Natural Language API

情緒分析運作方式的示意圖

運作方式

這個指令碼會從試算表收集文字,並連線至 Google Cloud Natural Language API,以便分析字串中的實體和情緒。樞紐分析表會針對所有文字資料列中提及的每個實體,匯總平均情緒分數。

Apps Script 服務

本解決方案會使用下列服務:

  • 試算表服務:將文字資料傳送至 Google Cloud Natural Language API,並在分析完每個資料列的情緒後,將其標示為「Complete」。
  • UrlFetch 服務:連線至 Google Cloud Natural Language API,對文字執行實體和情緒分析。

必要條件

如要使用這個範例,您必須具備下列先決條件:

  • Google 帳戶 (Google Workspace 帳戶可能需要管理員核准)。
  • 可連上網際網路的網路瀏覽器。

  • 已連結帳單帳戶的 Google Cloud 專案。請參閱「啟用專案的帳單功能」。

設定環境

在 Google Cloud 控制台中開啟 Cloud 專案

如果尚未開啟,請開啟要用於本範例的 Cloud 專案:

  1. 在 Google Cloud 控制台中,前往「Select a project」頁面。

    選取 Cloud 專案

  2. 選取要使用的 Google Cloud 專案。或者,您也可以按一下「建立專案」,然後按照畫面上的指示操作。如果您建立 Google Cloud 專案,可能需要為專案啟用計費功能

啟用 Google Cloud Natural Language API

這個解決方案會連結至 Google Cloud Natural Language API。使用 Google API 前,您必須先在 Google Cloud 專案中啟用這些 API。您可以在單一 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 Script 專案

  1. 點選下方按鈕,複製意見回饋情緒分析試算表範本。這個解決方案的 Apps Script 專案已附加至試算表。
    「建立副本」
  2. 依序按一下「擴充功能」>「Apps Script」
  3. 請在指令碼檔案中使用您的 API 金鑰更新下列變數:
    const myApiKey = 'YOUR_API_KEY'; // Replace with your API key.
  4. 按一下「儲存」圖示 「儲存」圖示

新增文字資料

  1. 返回試算表。
  2. 將文字資料新增至「id」和「comments」欄。您可以使用 Kaggle 的度假屋評論範例,也可以使用自己的資料。視需要新增更多資料欄,但為了順利執行,指令碼必須在 idcomments 資料欄中含有資料。

執行指令碼

  1. 依序按一下試算表頂端的「情緒分析工具」>「標記實體和情緒」。您可能需要重新整理頁面,才能顯示這項自訂選單。
  2. 出現提示時,請授權執行指令碼。如果 OAuth 同意畫面顯示「This app isn't verified」警告,請依序選取「Advanced」「Go to {Project Name} (unsafe)」(前往「{Project Name}」(不安全))。

  3. 依序點選「情緒分析工具」>「標記實體和情緒」

  4. 指令碼完成後,請切換至「資料透視表」工作表,查看結果。

查看程式碼

如要查看這個解決方案的 Apps Script 程式碼,請按一下下方的「查看原始碼」

查看原始碼

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 開發人員專家提供協助。

後續步驟