开发者问题收集

Google Apps Script API 单独迭代/循环通过 ID

2020-06-24
719

我有一个端点需要经过 X 次(取决于有多少个 ID),每次调用都需要分配其单独的 LineItem ID 并返回 JSON 响应。

我尝试了以下代码,似乎我可以调用 API,但似乎无法弄清楚如何将响应转换回我的工作表,因此在下面的情况下,我可能有最多 10 个 LI ID 需要单独调用 > 返回结果>复制到特定范围的最后一行,然后使用下一个 LI ID 进行下一个 API 调用,等等...

function ListLI360API_Agetest(){


  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('MySheet');
  var adID = 1558211;
  var LIs =sheet.getRange(2, 3, sheet.getLastRow(), 1).getValues().filter(String);
  var LIArrayLength = LIs.length;

    for (var i = 0; i <= LIArrayLength; i++) {

    if(LIs[i]!== undefined){
      var url = 'https://displayvideo.googleapis.com/v1/advertisers/'+adID+'/lineItems/'+LIs[i]+'/targetingTypes/TARGETING_TYPE_AGE_RANGE/assignedTargetingOptions'

  //Logger.log(url);
  var response = callApi5 (url, 'GET');
  //Logger.log(response);
  var content = response.getContentText();
  //Logger.log(content);
  var json = JSON.parse(content);
  //Logger.log(json);
  var ageData = json["assignedTargetingOptions"];
  //Logger.log(ageData);
   
  var rows = [],
      data;
  for (i = 0; i <= ageData.length; i++) {
        data = ageData[i];
        rows.push([data.name]);
      }
      //save results to spreadsheet in the next blank column and then API for next LI ID
      Logger.log(rows);
  
    }
  }//endfor
}

我似乎无法读取结果,我尝试将以下内容添加到上面的脚本中,但出现错误

"TypeError: Cannot read property "name" from undefined", im guessing there are some nulls/ blanks being returned in the JSON and hence it cant read the length

JSON 看起来像...

[20-06-24 21:34:57:159 BST] {
  "assignedTargetingOptions": [
    {
      "name": "advertisers/1558211/lineItems/36917016/targetingTypes/TARGETING_TYPE_AGE_RANGE/assignedTargetingOptions/503004",
      "assignedTargetingOptionId": "503004",
      "targetingType": "TARGETING_TYPE_AGE_RANGE",
      "inheritance": "NOT_INHERITED",
      "ageRangeDetails": {
        "ageRange": "AGE_RANGE_45_54",
        "targetingOptionId": "503004"
      }
    },
    {
      "name": "advertisers/1558211/lineItems/36917016/targetingTypes/TARGETING_TYPE_AGE_RANGE/assignedTargetingOptions/503005",
      "assignedTargetingOptionId": "503005",
      "targetingType": "TARGETING_TYPE_AGE_RANGE",
      "inheritance": "NOT_INHERITED",
      "ageRangeDetails": {
        "ageRange": "AGE_RANGE_55_64",
        "targetingOptionId": "503005"
      }
    },
    {
      "name": "advertisers/1558211/lineItems/36917016/targetingTypes/TARGETING_TYPE_AGE_RANGE/assignedTargetingOptions/503006",
      "assignedTargetingOptionId": "503006",
      "targetingType": "TARGETING_TYPE_AGE_RANGE",
      "inheritance": "NOT_INHERITED",
      "ageRangeDetails": {
        "ageRange": "AGE_RANGE_65_PLUS",
        "targetingOptionId": "503006"
      }
    }
  ]
}

[20-06-24 21:34:57:694 BST] {
  "assignedTargetingOptions": [
    {
      "name": "advertisers/1558211/lineItems/36917017/targetingTypes/TARGETING_TYPE_AGE_RANGE/assignedTargetingOptions/503004",
      "assignedTargetingOptionId": "503004",
      "targetingType": "TARGETING_TYPE_AGE_RANGE",
      "inheritance": "NOT_INHERITED",
      "ageRangeDetails": {
        "ageRange": "AGE_RANGE_45_54",
        "targetingOptionId": "503004"
      }
    },
    {
      "name": "advertisers/1558211/lineItems/36917017/targetingTypes/TARGETING_TYPE_AGE_RANGE/assignedTargetingOptions/503005",
      "assignedTargetingOptionId": "503005",
      "targetingType": "TARGETING_TYPE_AGE_RANGE",
      "inheritance": "NOT_INHERITED",
      "ageRangeDetails": {
        "ageRange": "AGE_RANGE_55_64",
        "targetingOptionId": "503005"
      }
    },
    {
      "name": "advertisers/1558211/lineItems/36917017/targetingTypes/TARGETING_TYPE_AGE_RANGE/assignedTargetingOptions/503006",
      "assignedTargetingOptionId": "503006",
      "targetingType": "TARGETING_TYPE_AGE_RANGE",
      "inheritance": "NOT_INHERITED",
      "ageRangeDetails": {
        "ageRange": "AGE_RANGE_65_PLUS",
        "targetingOptionId": "503006"
      }
    }
  ]
}

从这个例子中,有 2 个 LI ID,所以有 2 个单独的输出,我需要取出这些输出的部分并将它们打印到电子表格中

API 函数看起来像...

function callApi5(url, methodType, requestBody) {
  var service = getService();
  if (service.hasAccess()) {
  var headers = {
      'Content-Type': 'application/json',
      'Accept' :'application/json',
      'Authorization': 'Bearer ' + getService().getAccessToken()
  };
  var options = {
      method: methodType,
      headers : headers,
      muteHttpExceptions: true
  };
  if (requestBody) {
    options.payload = requestBody;
  }
  return UrlFetchApp.fetch(url, options);
  } else {
    var authorizationUrl = service.getAuthorizationUrl();
    Logger.log('Open the following URL and re-run the script: %s',
        authorizationUrl);
  }
}


function getService() {
  // Create a new service with the given name. The name will be used when
  // persisting the authorized token, so ensure it is unique within the
  // scope of the property store.
  return OAuth2.createService('MyService')

      // Set the endpoint URLs, which are the same for all Google services.
      .setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth')
      .setTokenUrl('https://accounts.google.com/o/oauth2/token')
      

      // Set the client ID and secret, from the Google Developers Console.
      .setClientId("xxxxx.apps.googleusercontent.com")
      .setClientSecret("xxxxxx")

      // Set the name of the callback function in the script referenced
      // above that should be invoked to complete the OAuth flow.
      .setCallbackFunction('authCallback')

      // Set the property store where authorized tokens should be persisted.
      .setPropertyStore(PropertiesService.getUserProperties())

      // Set the scopes to request (space-separated for Google services).
      // this is blogger read only scope for write access is:
      .setScope('https://www.googleapis.com/auth/display-video')
      // Below are Google-specific OAuth2 parameters.

      // Sets the login hint, which will prevent the account chooser screen
      // from being shown to users logged in with multiple accounts.
      .setParam('login_hint', '[email protected]')
      
      // Requests offline access.
      .setParam('access_type', 'offline')

      // Forces the approval prompt every time. This is useful for testing,
      // but not desirable in a production application.
      .setParam('approval_prompt', 'force');
}
2个回答

我认为您的目标如下。

  • 您想要从所有使用 'https://displayvideo.googleapis.com/v1/advertisers/'+adID+'/lineItems/'+LIs[i]+'/targetingTypes/TARGETING_TYPE_AGE_RANGE/assignedTargetingOptions' 创建的 URL 的请求中检索值,并将它们放入电子表格中。

对此,这个答案怎么样?从您的问题来看,我认为您的 callApi5() 脚本有效,并且 var json = JSON.parse(content);json 是您在问题中显示的值。因此我建议修改 ListLI360API_Agetest 函数。

修改点:

  • 当在for循环中使用数组时,请从 0 循环到 array.length - 1 。因为数组的第一个索引是 0 。所以,当使用 for (var i = 0; i <= LIArrayLength; i++) 时,在 LIArrayLength 的最后一个循环处会出现错误。在这种情况下,请修改为 for (var i = 0; i < LIArrayLength; i++) 。同样,对于 for (i = 0; i <= ageData.length; i++) { 也可以这样说。
  • 在您的脚本中,for循环中包含了1个for循环。而且,每个循环都使用变量 i 。在这种情况下,每个循环的 i 变量都会受到影响。因此,循环无法正常工作。
    • 我认为您的 TypeError: Cannot read property "name" from undefined 错误可能是由于以上 2 点造成的。
  • LIs 中的 var LIs =sheet.getRange(2, 3, sheet.getLastRow(), 1).getValues().filter(String); 是二维数组。所以在这种情况下,我认为 LIs[i][0]LIs[i] 更合适。

当以上几点反映到您的脚本中时,它变成如下形式。

修改后的脚本:

请复制并粘贴以下脚本,并将目标工作表名称设置为 ss.getSheetByName("###").getRange(1, 10, result.length, 1).setValues(result); 的最后一行。

function ListLI360API_Agetest(){
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getSheetByName('MySheet');  // Modified
  var adID = 1558211;
  var LIs = sheet.getRange(2, 3, sheet.getLastRow(), 1).getValues().filter(String);
  var LIArrayLength = LIs.length;
  var result = [];  // Added
  for (var i = 0; i < LIArrayLength; i++) {  // Modified
    if (LIs[i][0] !== undefined) {  // Modified
      var url = 'https://displayvideo.googleapis.com/v1/advertisers/'+adID+'/lineItems/'+LIs[i][0]+'/targetingTypes/TARGETING_TYPE_AGE_RANGE/assignedTargetingOptions'  // Modified
      var response = callApi5 (url, 'GET');
      var content = response.getContentText();
      var json = JSON.parse(content);
      var ageData = json["assignedTargetingOptions"];
      for (var j = 0; j < ageData.length; j++) {  // Modified
        var data = ageData[j];
        result.push([data.name]);  // Modified
      }
    }
  }
  // Please set the destination sheet name.
  ss.getSheetByName("###").getRange(1, 1, result.length, 1).setValues(result);  // Added
}
  • 如果 data.name 不存在,您不想输入值,请将 result.push([data.name]); 修改为 if (data.name) result.push([data.name]);

注意:

  • 在此修改后的脚本中,假设从每个 URL 检索的 JSON 对象的结构相同。如果 LIs[i][0] 创建的每个 URL 的结构不同,则需要修改脚本。请注意这一点。
  • 我无法从您的问题中理解将值放入电子表格的结果情况。因此在此修改后的脚本中,将值放入目标工作表。如果这与您的实际情况不同,请修改脚本。

参考:

Tanaike
2020-06-25

我测试了 Tanike 提供的答案,并修改了最后一部分以便能够打印到电子表格中。我从 JSON 中添加了更多字段来测试这一点,最后添加了:

dataRange = sheet.getRange(lr+1, 17, result.length,result[0].length).setValues(result);

以打印到电子表格上。

function ListLI360API_Agetest(){
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getSheetByName('MySheet');  // Modified
  var adID = 1558211;
  var LIs = sheet.getRange(2, 3, sheet.getLastRow(), 1).getValues().filter(String);
  var LIArrayLength = LIs.length;
  var result = [];  // Added
  var lr = sheet.getRange('Q1').getDataRegion().getLastRow(); //Added
  for (var i = 0; i < LIArrayLength; i++) {  // Modified
    if (LIs[i][0] !== undefined) {  // Modified
      var url = 'https://displayvideo.googleapis.com/v1/advertisers/'+adID+'/lineItems/'+LIs[i][0]+'/targetingTypes/TARGETING_TYPE_AGE_RANGE/assignedTargetingOptions'  // Modified
      var response = callApi5 (url, 'GET');
      var content = response.getContentText();
      var json = JSON.parse(content);
      var ageData = json["assignedTargetingOptions"];
      for (var j = 0; j < ageData.length; j++) {  // Modified
        var data = ageData[j];
        result.push([
        data.name,
        data.assignedTargetingOptionId,
        data.ageRangeDetails.ageRange]);  // Modified
      }
    }
  }
  // Each Set of results is pushed one after another
  dataRange = sheet.getRange(lr+1, 17, result.length,result[0].length).setValues(result);//Modified

}
Imdad Rahman
2020-06-25