You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

通过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 23:23:18