連結至 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 service - 連線至 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 專案中啟用這些 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. 查看您的應用程式註冊摘要。如要變更,請按一下「編輯」。如果應用程式註冊正確無誤,請按一下「Back to Dashboard」(返回資訊主頁)

取得 Google Cloud Natural Language API 的 API 金鑰

  1. 前往 Google Cloud 控制台。請確定已啟用計費功能的專案已開啟。
  2. 在 Google Cloud 控制台中,依序點選「選單」圖示 >「API 和服務」>「憑證」

    前往「憑證」頁面

  3. 依序按一下「建立憑證」>「API 金鑰」

  4. 記下 API 金鑰,在後續步驟中使用。

設定指令碼

建立 Apps Script 專案

  1. 點選下方按鈕即可複製「Sentiment Analysis for feedback」範例試算表。這項解決方案的 Apps Script 專案已附加到試算表。
    建立副本
  2. 依序按一下「Extensions」>「Apps Script」
  3. 使用 API 金鑰更新指令碼檔案中的下列變數:
    const myApiKey = 'YOUR_API_KEY'; // Replace with your API key.
  4. 按一下「「儲存」圖示」。

新增文字資料

  1. 返回試算表。
  2. idcomments 資料欄中加入文字資料。您可以使用 Kaggle 提供的度假屬性評論範例,也可以使用您自己的資料。您可以視需要新增更多資料欄,但若要成功執行,指令碼在 idcomments 欄中必須具有資料。

執行指令碼

  1. 在試算表頂端,依序按一下「情緒工具」>「標記實體和情緒」。您可能需要重新整理頁面,才能看到這個自訂選單。
  2. 出現提示訊息時,請授權指令碼。如果 OAuth 同意畫面顯示警告,請依序選取「Advanced」>「Go to {Project Name} (unsafe)」繼續操作。

  3. 再次按一下「Sentiment Tools」>「標示實體和情緒」

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

後續步驟