未记录的 Sheet API 限制问题
Undocumented Sheet API limitation problems
我已经看过有人遇到类似问题的帖子,但找不到明确的答案。
我尝试使用以下代码行检索包含 264735 个插槽的二维数组:
var optionalArguments = {majorDimension: "ROWS",
valueRenderOption: "FORMULA",
};
var sourceValuesObject = Sheets.Spreadsheets.Values.get(spreadsheetId, rangeA1Notation, optionalArguments)
但这是我得到的:
response code : 413. Message : response too large.
这看起来很奇怪,因为我看不到这种无处不在的限制,而且如果 300000 个或更少的单元格导致 API 错误,用户如何获取大量数据。
我试过拆分请求并且它有效,但这让我的代码变得更复杂和更慢,而且当我尝试将值更新回我的 sheet 范围时,我得到一个空响应。
我的方向是否正确,这是否正常,是否有解决方法?
编辑:
Here is a sample spreasheet
首先,我尝试使用 Sheets.Spreadsheets.Values.get
获取拆分范围
在 for
循环中并且有效。
用 batchGet
做同样的事情会给我同样的错误,所以我想我的单元格中的数据太大了。
这个解决方法怎么样?
实验:
此实验使用您共享的示例电子表格。
当UrlFetchApp 直接调用Sheets API 的端点时,如果响应大小大于50 MB (52,428,800 Byte),则返回小于50 MB 的响应。 50 MB 的大小是由于 UrlFetchApp 的限制。另一方面,在 Advanced Google Service 中,它无法确认这种情况,因为超过限制时会发生错误。所以通过使用UrlFetchApp,可以确认你的情况错误的原因。所以起初,我使用以下脚本确认了这一点。
var spreadsheetId = "#####";
var range = "'Copie de Feuille 1'!A1:JE1000";
var url = "https://sheets.googleapis.com/v4/spreadsheets/" + spreadsheetId + "/values/" + range + "?majorDimension=ROWS&valueRenderOption=FORMULA";
var res = UrlFetchApp.fetch(url, {headers: {Authorization: "Bearer " + ScriptApp.getOAuthToken()}});
Logger.log(res.getContentText().length.toString())
var values = JSON.parse(res.getContentText());
当上述脚本为运行时,返回52428450
。最后一行出现"Unterminated string literal"的错误。这意味着该对象是不完整的。从这个结果可以看出,'Copie de Feuille 1'!A1:JE1000
范围内的values.get调用一次是取不到值的。这与 .
的情况相同
在您的示例电子表格中,我们发现发生错误的范围边界是 'Copie de Feuille 1'!A1:JE722
。当尝试从范围 'Copie de Feuille 1'!A1:JE723
中检索值时,会发生错误。从 'Copie de Feuille 1'!A1:JE722
检索到的值的大小为 52,390,229 字节。这小于 50 MB(52,428,800 字节)。 'Copie de Feuille 1'!A1:JE723
的大小为 52,428,450 字节,与 'Copie de Feuille 1'!A1:JE1000
的值相同。由此发现是超出了UrlFetchApp的限制。
解决方法:
为了避免这个错误并检索所有值,作为一种解决方法,我想我想建议它拆分从电子表格检索值的范围。但在你的问题中,你想要速度。所以我想提出以下示例脚本。
- 创建请求。
- 使用
UrlFetchApp.fetchAll()
获取创建的请求。
- 通过
UrlFetchApp.fetchAll()
,每个请求都可以通过异步处理工作。
- 在 Advanced Google 服务的 Sheets API 中,无法使用。而且,在values.batchGet,因为所有检索到的值都超过限制,所以发生错误。
- 由此,使用
UrlFetchApp.fetchAll()
的处理成本低于高级 Google 服务的 Sheets API。
示例脚本:
var ranges = ["'Copie de Feuille 1'!A1:JE500", "'Copie de Feuille 1'!A501:JE1000"]; // This was used from the shared spreadsheet. So please modify this for your environment.
var token = ScriptApp.getOAuthToken();
var requests = ranges.map(function(e) {
return {
method: "get",
url: "https://sheets.googleapis.com/v4/spreadsheets/" + spreadsheetId + "/values/" + e + "?majorDimension=ROWS&valueRenderOption=FORMULA",
headers: {Authorization: "Bearer " + token},
muteHttpExceptions: true,
}
});
var res = UrlFetchApp.fetchAll(requests);
var values = res.reduce(function(ar, e) {
Array.prototype.push.apply(ar, JSON.parse(e.getContentText()).values);
return ar;
}, []);
Logger.log(values.length) // 1000
Logger.log(values[0].length) // 265
注:
- 如果您想使用该脚本,请确认在 API 控制台启用了 Sheets API。
- 数组合并时,如果出现数组限制错误,请使用每个数组,不要合并数组。
- 使用Advanced Google服务的Sheets API时,在
'Copie de Feuille 1'!A1:JE722
范围内也没有出现错误,在'Copie de Feuille 1'!A1:JE723
处出现错误。这个结果和UrlFetchApp
. 的结果是一样的
参考文献:
下面是我通过比较不同的获取值的方法所发现的:
function compare(){
getValuesSpreasheetApp();
getValuesUrlFetch();
getValues();
}
//using spreadsheetApp.getFormulas()
function getValuesSpreasheetApp(){
var t0 = new Date().getTime();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var dataRange = ss.getActiveSheet().getDataRange();
var formulas = dataRange.getFormulas();
var values = dataRange.getValues();
var t1 = new Date().getTime();
Logger.log("spreadsheetApp: " + (t1 -t0));
}
//using UrlFetchApp.fetchAll
function getValuesUrlFetch()
{
var t0 = new Date().getTime();
var ranges = ["'Copie de Feuille 1'!A1:JE500", "'Copie de Feuille 1'!A501:JE1000"];
var token = ScriptApp.getOAuthToken();
var spreadsheetId = SpreadsheetApp.getActiveSpreadsheet().getId();
var requests = ranges.map(function(e) {
return {
method: "get",
url: "https://sheets.googleapis.com/v4/spreadsheets/" + spreadsheetId + "/values/" + e + "?majorDimension=ROWS&valueRenderOption=FORMULA",
headers: {Authorization: "Bearer " + token},
muteHttpExceptions: true,
}
});
var res = UrlFetchApp.fetchAll(requests);
var values = res.reduce(function(ar, e) {
Array.prototype.push.apply(ar, JSON.parse(e.getContentText()).values);
return ar;
}, []);
var t1 = new Date().getTime();
Logger.log("UrlFetch: " + (t1 -t0));
}
//using Sheets.Spreadsheets.Values.get()
function getValues(){
var t0 = new Date().getTime();
var spreadsheetId = SpreadsheetApp.getActiveSpreadsheet().getId();
var ranges = ["'Copie de Feuille 1'!A1:JE500", "'Copie de Feuille 1'!A501:JE1000"];
var optionalArguments = {majorDimension: "ROWS",
valueRenderOption: "FORMULA",
};
res = [];
for(var i = 0; i < ranges.length; i++)
res.push(Sheets.Spreadsheets.Values.get(spreadsheetId, ranges[i]));
var values = res.reduce(function(ar, e) {
Array.prototype.push.apply(ar, e.values);
return ar;
}, []);
var t1 = new Date().getTime();
Logger.log("values.get: " + (t1 -t0));
}
结果如下:
[18-10-23 01:35:30:034 CEST] spreadsheetApp: 13828
[18-10-23 01:35:33:614 CEST] UrlFecth: 3580
[18-10-23 01:35:38:185 CEST] values.get: 4570
感谢@Tanaike在错误发生时给了我如此准确的答案,并为我提供了替代解决方案。
这是基准,fetchAll
是最快的解决方案。
我已经看过有人遇到类似问题的帖子,但找不到明确的答案。
我尝试使用以下代码行检索包含 264735 个插槽的二维数组:
var optionalArguments = {majorDimension: "ROWS",
valueRenderOption: "FORMULA",
};
var sourceValuesObject = Sheets.Spreadsheets.Values.get(spreadsheetId, rangeA1Notation, optionalArguments)
但这是我得到的:
response code : 413. Message : response too large.
这看起来很奇怪,因为我看不到这种无处不在的限制,而且如果 300000 个或更少的单元格导致 API 错误,用户如何获取大量数据。
我试过拆分请求并且它有效,但这让我的代码变得更复杂和更慢,而且当我尝试将值更新回我的 sheet 范围时,我得到一个空响应。
我的方向是否正确,这是否正常,是否有解决方法?
编辑: Here is a sample spreasheet
首先,我尝试使用 Sheets.Spreadsheets.Values.get
获取拆分范围
在 for
循环中并且有效。
用 batchGet
做同样的事情会给我同样的错误,所以我想我的单元格中的数据太大了。
这个解决方法怎么样?
实验:
此实验使用您共享的示例电子表格。
当UrlFetchApp 直接调用Sheets API 的端点时,如果响应大小大于50 MB (52,428,800 Byte),则返回小于50 MB 的响应。 50 MB 的大小是由于 UrlFetchApp 的限制。另一方面,在 Advanced Google Service 中,它无法确认这种情况,因为超过限制时会发生错误。所以通过使用UrlFetchApp,可以确认你的情况错误的原因。所以起初,我使用以下脚本确认了这一点。
var spreadsheetId = "#####";
var range = "'Copie de Feuille 1'!A1:JE1000";
var url = "https://sheets.googleapis.com/v4/spreadsheets/" + spreadsheetId + "/values/" + range + "?majorDimension=ROWS&valueRenderOption=FORMULA";
var res = UrlFetchApp.fetch(url, {headers: {Authorization: "Bearer " + ScriptApp.getOAuthToken()}});
Logger.log(res.getContentText().length.toString())
var values = JSON.parse(res.getContentText());
当上述脚本为运行时,返回52428450
。最后一行出现"Unterminated string literal"的错误。这意味着该对象是不完整的。从这个结果可以看出,'Copie de Feuille 1'!A1:JE1000
范围内的values.get调用一次是取不到值的。这与
在您的示例电子表格中,我们发现发生错误的范围边界是 'Copie de Feuille 1'!A1:JE722
。当尝试从范围 'Copie de Feuille 1'!A1:JE723
中检索值时,会发生错误。从 'Copie de Feuille 1'!A1:JE722
检索到的值的大小为 52,390,229 字节。这小于 50 MB(52,428,800 字节)。 'Copie de Feuille 1'!A1:JE723
的大小为 52,428,450 字节,与 'Copie de Feuille 1'!A1:JE1000
的值相同。由此发现是超出了UrlFetchApp的限制。
解决方法:
为了避免这个错误并检索所有值,作为一种解决方法,我想我想建议它拆分从电子表格检索值的范围。但在你的问题中,你想要速度。所以我想提出以下示例脚本。
- 创建请求。
- 使用
UrlFetchApp.fetchAll()
获取创建的请求。- 通过
UrlFetchApp.fetchAll()
,每个请求都可以通过异步处理工作。 - 在 Advanced Google 服务的 Sheets API 中,无法使用。而且,在values.batchGet,因为所有检索到的值都超过限制,所以发生错误。
- 由此,使用
UrlFetchApp.fetchAll()
的处理成本低于高级 Google 服务的 Sheets API。
- 通过
示例脚本:
var ranges = ["'Copie de Feuille 1'!A1:JE500", "'Copie de Feuille 1'!A501:JE1000"]; // This was used from the shared spreadsheet. So please modify this for your environment.
var token = ScriptApp.getOAuthToken();
var requests = ranges.map(function(e) {
return {
method: "get",
url: "https://sheets.googleapis.com/v4/spreadsheets/" + spreadsheetId + "/values/" + e + "?majorDimension=ROWS&valueRenderOption=FORMULA",
headers: {Authorization: "Bearer " + token},
muteHttpExceptions: true,
}
});
var res = UrlFetchApp.fetchAll(requests);
var values = res.reduce(function(ar, e) {
Array.prototype.push.apply(ar, JSON.parse(e.getContentText()).values);
return ar;
}, []);
Logger.log(values.length) // 1000
Logger.log(values[0].length) // 265
注:
- 如果您想使用该脚本,请确认在 API 控制台启用了 Sheets API。
- 数组合并时,如果出现数组限制错误,请使用每个数组,不要合并数组。
- 使用Advanced Google服务的Sheets API时,在
'Copie de Feuille 1'!A1:JE722
范围内也没有出现错误,在'Copie de Feuille 1'!A1:JE723
处出现错误。这个结果和UrlFetchApp
. 的结果是一样的
参考文献:
下面是我通过比较不同的获取值的方法所发现的:
function compare(){
getValuesSpreasheetApp();
getValuesUrlFetch();
getValues();
}
//using spreadsheetApp.getFormulas()
function getValuesSpreasheetApp(){
var t0 = new Date().getTime();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var dataRange = ss.getActiveSheet().getDataRange();
var formulas = dataRange.getFormulas();
var values = dataRange.getValues();
var t1 = new Date().getTime();
Logger.log("spreadsheetApp: " + (t1 -t0));
}
//using UrlFetchApp.fetchAll
function getValuesUrlFetch()
{
var t0 = new Date().getTime();
var ranges = ["'Copie de Feuille 1'!A1:JE500", "'Copie de Feuille 1'!A501:JE1000"];
var token = ScriptApp.getOAuthToken();
var spreadsheetId = SpreadsheetApp.getActiveSpreadsheet().getId();
var requests = ranges.map(function(e) {
return {
method: "get",
url: "https://sheets.googleapis.com/v4/spreadsheets/" + spreadsheetId + "/values/" + e + "?majorDimension=ROWS&valueRenderOption=FORMULA",
headers: {Authorization: "Bearer " + token},
muteHttpExceptions: true,
}
});
var res = UrlFetchApp.fetchAll(requests);
var values = res.reduce(function(ar, e) {
Array.prototype.push.apply(ar, JSON.parse(e.getContentText()).values);
return ar;
}, []);
var t1 = new Date().getTime();
Logger.log("UrlFetch: " + (t1 -t0));
}
//using Sheets.Spreadsheets.Values.get()
function getValues(){
var t0 = new Date().getTime();
var spreadsheetId = SpreadsheetApp.getActiveSpreadsheet().getId();
var ranges = ["'Copie de Feuille 1'!A1:JE500", "'Copie de Feuille 1'!A501:JE1000"];
var optionalArguments = {majorDimension: "ROWS",
valueRenderOption: "FORMULA",
};
res = [];
for(var i = 0; i < ranges.length; i++)
res.push(Sheets.Spreadsheets.Values.get(spreadsheetId, ranges[i]));
var values = res.reduce(function(ar, e) {
Array.prototype.push.apply(ar, e.values);
return ar;
}, []);
var t1 = new Date().getTime();
Logger.log("values.get: " + (t1 -t0));
}
结果如下:
[18-10-23 01:35:30:034 CEST] spreadsheetApp: 13828
[18-10-23 01:35:33:614 CEST] UrlFecth: 3580
[18-10-23 01:35:38:185 CEST] values.get: 4570
感谢@Tanaike在错误发生时给了我如此准确的答案,并为我提供了替代解决方案。
这是基准,fetchAll
是最快的解决方案。