Apps 脚本自定义函数在脚本编辑器中工作,但在 Google Sheet 自定义函数中不起作用

Apps Script custom function working in script editor but not in Google Sheet custom function

我使用 URLFetchApp 在 Apps 脚本中构建了一个简单的自定义函数来获取 TikTok 帐户的关注者数量。

function tiktok_fans() {
  var raw_data = new RegExp(/("followerCount":)([0-9]+)/g);
  var handle = '@charlidamelio';
  var web_content = UrlFetchApp.fetch('https://www.tiktok.com/'+ handle + '?lang=en').getContentText();
  var match_text = raw_data.exec(web_content);
  var result = (match_text[2]);
  Logger.log(result)
  return result
}

日志返回正确的关注者数量。

但是,当我将代码更改为;

function tiktok_fans(handle) {
  var raw_data = new RegExp(/("followerCount":)([0-9]+)/g);
  //var handle = '@charlidamelio';
  var web_content = UrlFetchApp.fetch('https://www.tiktok.com/'+ handle + '?lang=en').getContentText();
  var match_text = raw_data.exec(web_content);
  var result = (match_text[2]);
  Logger.log(result)
  return result
}

并在电子表格中使用它,例如 =tiktok_fans(A1),其中 A1 有 @charlidamelio 我在单元格

中得到 #ERROR 响应

TypeError: Cannot read property '2' of null (line 6).

为什么它在日志中有效但在电子表格中无效?

--附加信息--

在测试下面的 @Tanaike 答案后仍然出现相同的错误,"TypeError: Cannot read 属性 '2' of null (line 6)."

已经手动映射出来看到错误,下面每次运行时,不同的日志returns“null”。我相信这与 ContentText size/in 缓存有关。我曾尝试在函数之间使用 Utilities.sleep() 但没有成功,我仍然得到空值。

代码

  var raw_data = new RegExp(/("followerCount":)([0-9]+)/g);

  //tiktok urls
  var qld = UrlFetchApp.fetch('https://www.tiktok.com/@thisisqueensland?lang=en').getContentText();
  var nsw = UrlFetchApp.fetch('https://www.tiktok.com/@visitnsw?lang=en').getContentText();
  var syd = UrlFetchApp.fetch('https://www.tiktok.com/@sydney?lang=en').getContentText();
  var tas = UrlFetchApp.fetch('https://www.tiktok.com/@tasmania?lang=en').getContentText();
  var nt = UrlFetchApp.fetch('https://www.tiktok.com/@ntaustralia?lang=en').getContentText();
  var nz = UrlFetchApp.fetch('https://www.tiktok.com/@purenz?lang=en').getContentText();
  var aus = UrlFetchApp.fetch('https://www.tiktok.com/@australia?lang=en').getContentText();
  var vic = UrlFetchApp.fetch('https://www.tiktok.com/@visitmelbourne?lang=en').getContentText();

  //find folowers with regex
  var match_qld = raw_data.exec(qld);
  var match_nsw = raw_data.exec(nsw);
  var match_syd = raw_data.exec(syd);
  var match_tas = raw_data.exec(tas);
  var match_nt = raw_data.exec(nt);
  var match_nz = raw_data.exec(nz);
  var match_aus = raw_data.exec(aus);
  var match_vic = raw_data.exec(vic);

  Logger.log(match_qld);
  Logger.log(match_nsw);
  Logger.log(match_syd);
  Logger.log(match_tas);
  Logger.log(match_nt);
  Logger.log(match_nz);
  Logger.log(match_aus);
  Logger.log(match_vic);

这对我来说是一个自定义函数:

function MYFUNK(n=2) {
  const url = 'my website url'
  const re = new RegExp(`<p id="un${n}.*\/p>`,'g')
  const r = UrlFetchApp.fetch(url).getContentText();
  const v = r.match(re);
  Logger.log(v);
  return v;
}

我使用了我自己的网站,我有几个段落的 ID 从 un1 到 un7,我将 A1 的值作为唯一参数。它 returns 每次更改都是正确的字符串。

问题:

根据你的情况,我想起了自定义函数的UrlFetchApp请求和脚本编辑器的UrlFetchApp请求是不一样的。所以我认为您的问题的原因可能与此线程有关。 以你的情况来看,你的情况好像和这个帖子相反。但是,这个问题被认为是由于网站的规范。

为了检查这个差异,我检查了检索到的 HTML 数据的文件大小。

  • 使用脚本编辑器执行的 UrlFetchApp 检索到的 HTML 数据的文件大小为 518k 字节。
  • 使用自定义函数执行的UrlFetchApp 检索到的HTML 数据的文件大小为9k 字节。
    • 使用自定义函数执行的UrlFetchApp请求与使用Web Apps执行的UrlFetchApp请求似乎是一样的。 9k bytes的数据就是通过这个获取的。

从上面的结果,发现检索到的HTML在脚本编辑器和自定义函数之间是不一样的。即,自定义函数检索到的 HTML 数据不包括 ("followerCount":)([0-9]+) 的正则表达式。这样,就会发生这样的错误。我认为这可能是您遇到问题的原因。

解决方法:

当我使用 Web 应用程序和触发器测试您的情况时,出现了同样的问题。至此,在目前阶段,我认为自动执行脚本的方法可能无法使用。那么,作为解决方法,使用按钮和自定义菜单怎么样?当脚本被按钮和自定义菜单 运行 时,脚本工作。看来这个方法和脚本编辑器的方法是一样的

示例脚本如下

示例脚本:

在你运行脚本之前,请设置range。例如,请将此功能分配给电子表格上的按钮。单击按钮时,脚本为 运行。在此示例中,它假设像 @charlidamelio 这样的值被放入“A”列。

function sample() {
  var range = "A2:A10"; // Please set the range of "handle".
  var raw_data = new RegExp(/("followerCount":)([0-9]+)/g);
  var sheet = SpreadsheetApp.getActiveSheet();
  var r = sheet.getRange(range);
  var values = r.getValues();
  var res = values.map(([handle]) => {
    if (handle != "") {
      var web_content = UrlFetchApp.fetch('https://www.tiktok.com/'+ handle + '?lang=en').getContentText();
      var match_text = raw_data.exec(web_content);
      return [match_text[2]];
    }
    return [""];
  });
  r.offset(0, 1).setValues(res);
}
  • 当此脚本为 运行 时,将从 URL 中检索值并将其放入“B”列。

注:

  • 这是一个简单的脚本。所以请根据自己的实际情况修改。

参考:

  • 相关线程。

已添加:

关于以下附加问题,

whilst this works for 1 TikTok handle, when trying to run a list of multiple it fails each time, with the error TypeError: Cannot read property '2' of null. After doing some investigating and manually mapping out 8 handles, I can see that each time it runs, it returns "null" for one or more of the web_content variables. Is there a way to slow the script down/run each UrlFetchApp one at a time to ensure each returns content?

i've tried this and still getting an error. Have tried up to 10000ms. I've added some more detail to the original question, hope this makes sense as to the error. It is always in a different log that I get nulls, hence why I think it's a timing or cache issue.

在这种情况下,下面的示例脚本怎么样?

示例脚本:

在此示例脚本中,当无法从 URL 中检索到该值时,将尝试再次检索该值作为重试。此示例脚本使用 2 次作为重试。所以当2次重试都取不到值时,返回空值。

function sample() {
  var range = "A2:A10"; // Please set the range of "handle".
  var raw_data = new RegExp(/("followerCount":)([0-9]+)/g);
  var sheet = SpreadsheetApp.getActiveSheet();
  var r = sheet.getRange(range);
  var values = r.getValues();
  var res = values.map(([handle]) => {
    if (handle != "") {
      var web_content = UrlFetchApp.fetch('https://www.tiktok.com/'+ handle + '?lang=en').getContentText();
      var match_text = raw_data.exec(web_content);
      if (!match_text || match_text.length != 3) {
        var retry = 2; // Number of retry.
        for (var i = 0; i < retry; i++) {
          Utilities.sleep(3000);
          web_content = UrlFetchApp.fetch('https://www.tiktok.com/'+ handle + '?lang=en').getContentText();
          match_text = raw_data.exec(web_content);
          if (match_text || match_text.length == 3) break;
        }
      }
      return [match_text && match_text.length == 3 ? match_text[2] : ""];
    }
    return [""];
  });
  r.offset(0, 1).setValues(res);
}
  • 请调整retryUtilities.sleep(3000)的值。