通过Google Apps Script调用Tableau视图并同步数据至谷歌表格的问题
解决Google Apps Script连接Tableau API的问题
1. 修复认证请求的Header设置
你的代码中accept参数未放在headers对象内,导致Tableau服务器未正确识别JSON格式请求,默认返回XML。调整后的请求配置如下:
function tableauAPI() { const options = { method: 'post', muteHttpExceptions: true, contentType: 'application/json', headers: { 'Accept': 'application/json' // 将Accept头移入headers对象 }, payload: JSON.stringify({ credentials: { personalAccessTokenName: 'TestToken', personalAccessTokenSecret: '11122223333444455555', site: { contentUrl: '', // 默认站点留空即可 }, }, }), }; const response = UrlFetchApp.fetch( 'https://example.tableau.com/api/3.21/auth/signin', options ); console.log(response.getContentText()); }
若调整后仍返回XML,可使用XML解析工具提取认证信息。
2. 解析XML格式的认证令牌
借助Google Apps Script内置的XmlService解析XML响应:
function parseTableauAuthXML(xmlText) { const document = XmlService.parse(xmlText); const root = document.getRootElement(); const namespace = root.getNamespace(); // 提取认证Token const token = root.getChild('credentials', namespace).getAttribute('token').getValue(); // 提取站点ID和内容路径 const site = root.getChild('site', namespace); const siteId = site.getAttribute('id').getValue(); const contentUrl = site.getAttribute('contentUrl').getValue(); return { token, siteId, contentUrl }; }
在认证请求后调用该解析函数:
const response = UrlFetchApp.fetch('https://example.tableau.com/api/3.21/auth/signin', options); const authData = parseTableauAuthXML(response.getContentText()); console.log('认证Token:', authData.token);
3. 发起二次请求获取视图数据并写入Google Sheet
3.1 获取视图ID(已知ID可跳过)
调用API获取视图列表,定位目标视图ID:
function getViews(siteId, token, serverUrl) { const options = { method: 'get', headers: { 'X-Tableau-Auth': token } }; const response = UrlFetchApp.fetch(`${serverUrl}/api/3.21/sites/${siteId}/views`, options); return JSON.parse(response.getContentText()); }
3.2 查询视图数据并写入Sheet
function fetchViewDataToSheet(siteId, viewId, token, serverUrl, sheetId) { const options = { method: 'get', headers: { 'X-Tableau-Auth': token, 'Accept': 'application/json' } }; // 请求视图数据 const response = UrlFetchApp.fetch(`${serverUrl}/api/3.21/sites/${siteId}/views/${viewId}/data`, options); const data = JSON.parse(response.getContentText()); // 转换为Sheet可接受的二维数组格式 const columns = data.columns.map(col => col.fieldName); const rows = data.data.map(row => row.values); const sheetData = [columns, ...rows]; // 写入目标Sheet const sheet = SpreadsheetApp.openById(sheetId).getActiveSheet(); sheet.clear(); sheet.getRange(1, 1, sheetData.length, sheetData[0].length).setValues(sheetData); }
4. 完整整合代码
function tableauAPI() { const serverUrl = 'https://example.tableau.com'; const sheetId = '你的Google Sheet ID'; // 替换为实际Sheet ID const viewId = '你的目标视图ID'; // 替换为实际视图ID // 1. 发起认证请求 const authOptions = { method: 'post', muteHttpExceptions: true, contentType: 'application/json', headers: { 'Accept': 'application/json' }, payload: JSON.stringify({ credentials: { personalAccessTokenName: 'TestToken', personalAccessTokenSecret: '11122223333444455555', site: { contentUrl: '', }, }, }), }; const authResponse = UrlFetchApp.fetch(`${serverUrl}/api/3.21/auth/signin`, authOptions); let authData; try { // 优先尝试JSON解析 const jsonResponse = JSON.parse(authResponse.getContentText()); authData = { token: jsonResponse.credentials.token, siteId: jsonResponse.site.id }; } catch (e) { // JSON解析失败则用XML解析 authData = parseTableauAuthXML(authResponse.getContentText()); } // 2. 获取视图数据并写入Sheet fetchViewDataToSheet(authData.siteId, viewId, authData.token, serverUrl, sheetId); // 3. 注销认证(推荐执行) const signoutOptions = { method: 'post', headers: { 'X-Tableau-Auth': authData.token } }; UrlFetchApp.fetch(`${serverUrl}/api/3.21/auth/signout`, signoutOptions); } // XML解析辅助函数 function parseTableauAuthXML(xmlText) { const document = XmlService.parse(xmlText); const root = document.getRootElement(); const namespace = root.getNamespace(); const credentials = root.getChild('credentials', namespace); const token = credentials.getAttribute('token').getValue(); const site = root.getChild('site', namespace); const siteId = site.getAttribute('id').getValue(); return { token, siteId }; } // 写入Sheet辅助函数 function fetchViewDataToSheet(siteId, viewId, token, serverUrl, sheetId) { const options = { method: 'get', headers: { 'X-Tableau-Auth': token, 'Accept': 'application/json' } }; const response = UrlFetchApp.fetch(`${serverUrl}/api/3.21/sites/${siteId}/views/${viewId}/data`, options); const data = JSON.parse(response.getContentText()); const columns = data.columns.map(col => col.fieldName); const rows = data.data.map(row => row.values); const sheetData = [columns, ...rows]; const sheet = SpreadsheetApp.openById(sheetId).getActiveSheet(); sheet.clear(); sheet.getRange(1, 1, sheetData.length, sheetData[0].length).setValues(sheetData); }
注意事项
- 替换代码中
serverUrl、personalAccessTokenName、personalAccessTokenSecret、sheetId和viewId为你的实际信息。 - 确保Tableau API版本号(3.21)与你的服务器版本匹配,避免兼容性问题。
- 确认Personal Access Token拥有目标视图的访问权限。
内容的提问来源于stack exchange,提问作者Gary
相关产品推荐
相关产品推荐

