UrlFetchApp 请求在菜单函数中失败但在自定义函数中失败(连接到外部 REST API)

UrlFetchApp request fails in Menu Functions but not in Custom Functions (connecting to external REST API)

我正在使用以下函数连接到外部 API (Binance),使用 Google Apps 脚本检索 JSON 数组(市场价格)。这个简单的查询 url 在浏览器中运行良好(不需要 API 键):

function getMyArray() {
  var url ="https://api.binance.com/api/v3/ticker/price"; // works perfectly in browser
  var params =  {"method"  : "get",  "muteHttpExceptions":true };  
  var response = UrlFetchApp.fetch(url, params);
  var array = JSON.parse(response.getContentText());
  
  return array;
}

然而,当我尝试 运行 Google Apps 脚本中的函数时,情况就不同了:

  1. 脚本编辑器:在脚本编辑器中 运行 简单实用,但我收到 403 error“请求被阻止”
  2. 菜单函数:从添加到电子表格的菜单项调用函数UI => 相同403 error
  3. 自定义函数:编辑任何单元格并键入 =getMyArray() => 请求有效,我可以使用 Logger
  4. 跟踪数组

为什么我的简单请求在从菜单或脚本编辑器调用时被阻止,是否可以更改?谢谢

当自定义函数和脚本编辑器使用UrlFetchApp时,我认为区别在于是否使用IPv6,而IPv4的地址每隔运行改变一次。在这种情况下,脚本编辑器和自定义菜单的结果是相同的。我认为这可能是您遇到问题的原因。但我不确定我的猜测是否正确。因此,在这个答案中,我想提出以下解决方法。

  1. 使用脚本将公式 =getMyArray() 放入单元格。
    • 由此,值被检索到单元格。
  2. 使用脚本从单元格中检索值。
  3. 清除 put 公式。

通过这个流程,我认为你的目标可以达到。

示例脚本如下

示例脚本:

在此脚本中,作为测试,=getMyArray() 被放入活动 sheet 上的单元格“A1”,并从该单元格中检索值。使用时,请在脚本编辑器和自定义菜单中运行函数main()。这样,可以将值检索到 array.

function getMyArray() {
  var url = "https://api.binance.com/api/v3/ticker/price";
  var params =  {"method": "get", "muteHttpExceptions": true};
  var response = UrlFetchApp.fetch(url, params);
  return response.getContentText();
}

// Please run this function by the script editor and the custom menu.
function main() {
  var sheet = SpreadsheetApp.getActiveSheet();
  var range = sheet.getRange("A1");
  range.setFormula("=getMyArray()");
  SpreadsheetApp.flush();
  var value = range.getValue();
  range.clearContent();
  var array = JSON.parse(value);
  console.log(array)
}

参考文献:

已添加:

来自https://httpbin.org/get的响应值如下。

用于测试的示例脚本:

function sample() {
  var url = "https://httpbin.org/get";
  var res = UrlFetchApp.fetch(url);
  console.log(res.getContentText())
  return res.getContentText();
}

结果:

模式 1. 脚本是 运行 与脚本编辑器。
{
  "args": {}, 
  "headers": {
    "Accept-Encoding": "gzip,deflate,br", 
    "Host": "httpbin.org", 
    "User-Agent": "Mozilla/5.0 (compatible; Google-Apps-Script; beanserver; +https://script.google.com; id: ###)", 
    "X-Amzn-Trace-Id": "Root=###"
  }, 
  "origin": "### IPV6 ###, ### IPV4 ###", // or "### IPV4 ###, ### IPV4 ###"
  "url": "https://httpbin.org/get"
}
  • 当您使用 IPV6 时,origin"### IPV6 ###, ### IPV4 ###"。但是当你使用 IPV4 时,origin"### IPV4 ###, ### IPV4 ###".
  • 在这种情况下,无法从 https://api.binance.com/api/v3/ticker/price 中检索到正确的值。
模式 2. 脚本是带有自定义函数的 运行。

在这种情况下,=sample() 被放入单元格并检索值。

{
  "args": {}, 
  "headers": {
    "Accept-Encoding": "gzip,deflate,br", 
    "Host": "httpbin.org", 
    "User-Agent": "Mozilla/5.0 (compatible; Google-Apps-Script; beanserver; +https://script.google.com; id: ###)", 
    "X-Amzn-Trace-Id": "Root=###"
  }, 
  "origin": "### IPV4 ###", 
  "url": "https://httpbin.org/get"
}
  • 在这种情况下,可以从 https://api.binance.com/api/v3/ticker/price 中检索到正确的值。
模式 3. 脚本是 运行,带有 OnEdit 事件触发器。

UrlFetchApp与自定义函数一起使用时,不需要授权。但是当 UrlFetchApp 与 OnEdit 事件触发器一起使用时,授权需要安装触发器。我认为此授权可能会出现此问题。所以我比较了这个。

UrlFetchApp 与可安装的 OnEdit 事件触发器一起使用时,将检索到以下结果。

{
  "args": {}, 
  "headers": {
    "Accept-Encoding": "gzip,deflate,br", 
    "Host": "httpbin.org", 
    "User-Agent": "Mozilla/5.0 (compatible; Google-Apps-Script; beanserver; +https://script.google.com; id: ###)", 
    "X-Amzn-Trace-Id": "Root=###"
  }, 
  "origin": "### IPV4 ###", 
  "url": "https://httpbin.org/get"
}
  • 这个结果和上面的模式2一样
  • 在这种情况下,可以从 https://api.binance.com/api/v3/ticker/price 中检索到正确的值。

结果:

  • 包括User-Agent在内的headers对于所有模式都是一样的。
  • 从模式2和模式3来看,与Google端的授权无关。
  • 检索带有 IPV4 的 WHOIS 时,返回相同的结果。
    • origin"### IPV4 ###, ### IPV4 ###"时,第二个IPV4为Google的IP地址。

从上面的结果来看,所有模式的不同之处在于origin的值是1还是2。