Tableau REST API と Google Apps Script を使って Tableau Cloud のビューデータを Google Drive に自動連携してみた
こんにちは、ikumiです。
Tableauのサブスクリプション機能では、通常PDFなどのイメージ画像しかメール配信ができません。しかし、Tableau Cloud で作成したダッシュボードビューのデータを定期的に CSV でエクスポートして共有したい、という場面があります。
今回は、 Tableau REST API と Google Apps Script(GAS) を使用して、Tableau Cloud の通常のシートビューのデータを API で取得し、そのまま Google Drive の指定フォルダに CSV を自動保存する仕組みを試してみました。
参考:Tableau REST API:query_view_data
技術的アプローチ
Google Apps Script(GAS)から Tableau REST API を呼び出す構成です。
処理の流れ:
- GAS から Tableau REST API にサインインしてトークンを取得
- 取得したトークンを使ってビューの CSV データを取得
- 取得した CSV を Google Drive の指定フォルダに保存
検証環境について
- Tableau Cloud: 取得したいビューを作成済み
- Tableau Cloud 権限: 対象ビューへのアクセス権があり、個人用アクセストークンを取得できる
- Google アカウント: Google Drive・Google Apps Script が利用できること
事前準備
1. Tableau Cloud で PAT(パーソナルアクセストークン)を発行する
Tableau Cloud の右上アカウントメニューから マイアカウントの設定 を開き、パーソナルアクセストークン セクションでトークンを作成します。


表示される トークン名 と トークンシークレット をメモしておきます。シークレットはこの画面でしか確認できないため、必ずコピーして保管してください。
2. ビューの ID(LUID)を確認する
Tableau REST API ではビューを LUID(一意のID)で指定します。
※後述の GAS の findViewByName() ヘルパー関数を実行することで、ビュー名で絞り込んで LUID を確認できます。
3. Google Drive のフォルダ ID を確認する
CSV を保存したい Google Drive のフォルダを開き、URL の末尾の文字列をコピーします。
https://drive.google.com/drive/folders/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
↑ この部分がフォルダID
4. Google Apps Script プロジェクトを作成する
Google Drive 上で 新規 → その他 → Google Apps Script を選択してプロジェクトを作成します。

試してみた
1. GAS にコードを貼り付ける
Google Apps Script エディタの中身を全選択(Ctrl+A)して削除し、以下のコードを貼り付けます。冒頭の CONFIG の値を自身の環境に合わせて書き換えてください。
GASコードを展開する
// ===== 設定値(★要変更★の箇所を書き換えてください) =====
const CONFIG = {
TABLEAU_URL: 'https://prod-apnortheast-a.online.tableau.com', // ★要変更★ Tableau Cloud の URL(末尾スラッシュなし)
API_VERSION: '3.28',
SITE_CONTENT_URL: 'your-site-name', // ★要変更★ URL の /site/〇〇/ の〇〇
PAT_NAME: 'your-pat-name', // ★要変更★ PAT のトークン名
PAT_SECRET: 'your-pat-secret', // ★要変更★ PAT のトークンシークレット
VIEW_ID: 'your-view-id', // ★要変更★ ビューの LUID(後述の手順で確認)
DRIVE_FOLDER_ID: 'your-folder-id', // ★要変更★ Google Drive フォルダの ID
FILE_NAME: 'tableau_export.csv', // 保存するファイル名
};
// ===== メイン処理(これを実行する) =====
function exportTableauViewToGoogleDrive() {
// Step 1: Tableau Cloud にサインイン
const auth = signIn();
if (!auth.token) {
Logger.log('サインイン失敗。PAT_NAME・PAT_SECRET・SITE_CONTENT_URL を確認してください');
return;
}
Logger.log('サインイン成功');
// Step 2: ビューの CSV データを取得
const blob = getViewData(auth.token, auth.siteId);
Logger.log('CSV データ取得成功');
// Step 3: Google Drive に保存
saveToGoogleDrive(blob);
Logger.log('Google Drive への保存完了!');
}
// Tableau Cloud にサインインしてトークンを取得する
function signIn() {
const url = `${CONFIG.TABLEAU_URL}/api/${CONFIG.API_VERSION}/auth/signin`;
const payload = `<tsRequest>
<credentials personalAccessTokenName="${CONFIG.PAT_NAME}"
personalAccessTokenSecret="${CONFIG.PAT_SECRET}">
<site contentUrl="${CONFIG.SITE_CONTENT_URL}" />
</credentials>
</tsRequest>`;
const response = UrlFetchApp.fetch(url, {
method: 'post',
contentType: 'application/xml',
payload: payload,
muteHttpExceptions: true,
});
const text = response.getContentText();
const tokenMatch = text.match(/token="([^"]+)"/);
const siteIdMatch = text.match(/site id="([^"]+)"/);
if (!tokenMatch || !siteIdMatch) {
Logger.log('認証エラー: ' + text);
return {};
}
return { token: tokenMatch[1], siteId: siteIdMatch[1] };
}
// ビューの CSV データを blob で取得する
function getViewData(token, siteId) {
const url = `${CONFIG.TABLEAU_URL}/api/${CONFIG.API_VERSION}/sites/${siteId}/views/${CONFIG.VIEW_ID}/data`;
const response = UrlFetchApp.fetch(url, {
method: 'get',
headers: { 'X-Tableau-Auth': token },
muteHttpExceptions: true,
});
return response.getBlob(); // 文字化け防止のためバイナリのまま取得
}
// CSV データを Google Drive の指定フォルダに保存する
function saveToGoogleDrive(blob) {
const folder = DriveApp.getFolderById(CONFIG.DRIVE_FOLDER_ID);
// 同名ファイルが既にある場合はゴミ箱へ移動
const existingFiles = folder.getFilesByName(CONFIG.FILE_NAME);
while (existingFiles.hasNext()) {
existingFiles.next().setTrashed(true);
}
blob.setName(CONFIG.FILE_NAME);
folder.createFile(blob); // blob のまま保存することで文字化けを防ぐ
}
// ビューの LUID を名前で検索するヘルパー関数
function findViewByName() {
const auth = signIn();
const url = `${CONFIG.TABLEAU_URL}/api/${CONFIG.API_VERSION}/sites/${auth.siteId}/views?filter=name:eq:ビュー名`;
// ↑ 「ビュー名」を実際のシート名に変えて実行してください
const response = UrlFetchApp.fetch(url, {
method: 'get',
headers: { 'X-Tableau-Auth': auth.token },
});
Logger.log(response.getContentText());
}
2. ビューの LUID を確認する
findViewByName 関数のコメント内「ビュー名」を取得したいシート名に書き換えてから実行します。

ログに XML 形式でビューの情報が出力されます。view id="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" の部分が LUID ですので、確認した LUID を CONFIG の VIEW_ID に設定します。

3. メイン関数を実行して動作確認する
exportTableauViewToGoogleDrive 関数を選択して実行します。初回実行時は Google Drive へのアクセス許可を求めるダイアログが表示されるので、許可してください。
実行後、ログに「Google Drive への保存完了!」と表示されれば問題ありません。

Google Drive の指定フォルダに CSV ファイルが作成されており、中身が正しいデータになっていれば完了です。
▼ダッシュボード上のデータ

▼出力結果


出力結果を確認すると、内容は正しいもののメジャーが縦持ちになっていてフォーマットが崩れていました・・。
おまけ. 出力結果のピボット
前段で起きていたフォーマット崩れの課題に対して、今回はGASでデータ整形をすることにしましたので、ビューデータ取得部分を以下とおりに修正することで、正しいフォーマットで出力することができました。
GASコードを展開する
// ===== ビューデータ取得 =====
function getViewData(token, siteId) {
const url = `${CONFIG.TABLEAU_URL}/api/${CONFIG.API_VERSION}/sites/${siteId}/views/${CONFIG.VIEW_ID}/data`;
const response = UrlFetchApp.fetch(url, {
method: 'get',
headers: { 'X-Tableau-Auth': token },
muteHttpExceptions: true,
});
return response.getBlob();
}
// ===== メジャーを横持に変換する =====
function pivotMeasures(csvText) {
const rows = Utilities.parseCsv(csvText);
const headers = rows[0];
const measureNameIdx = headers.findIndex(h => h.includes('メジャー ネーム'));
const measureValueIdx = headers.findIndex(h => h.includes('メジャー バリュー'));
// メジャー列が見つからない場合はそのまま返す
if (measureNameIdx === -1 || measureValueIdx === -1) {
Logger.log('メジャー列が見つからないため変換をスキップします');
return csvText;
}
const keyIndices = headers.map((_, i) => i).filter(i => i !== measureNameIdx && i !== measureValueIdx);
// ユニークなメジャー名を取得(出現順を保持)
const measureNames = [...new Set(rows.slice(1).map(r => r[measureNameIdx]))];
// キーごとにデータを集約
const pivotMap = new Map();
rows.slice(1).forEach(row => {
const key = keyIndices.map(i => row[i]).join('\t');
if (!pivotMap.has(key)) pivotMap.set(key, {});
pivotMap.get(key)[row[measureNameIdx]] = row[measureValueIdx];
});
// 出力ヘッダー・行を組み立て
const keyHeaders = keyIndices.map(i => headers[i]);
const outputHeaders = [...keyHeaders, ...measureNames];
const outputRows = [outputHeaders];
pivotMap.forEach((measures, key) => {
const keyValues = key.split('\t');
const row = [...keyValues, ...measureNames.map(m => measures[m] || '')];
outputRows.push(row);
});
return outputRows.map(r => r.map(v => `"${v}"`).join(',')).join('\n');
}

最後に
Tableau REST API と Google Apps Script を組み合わせて、Tableau Cloud のビューデータを Google Drive に自動連携する仕組みを作ってみました。
GAS のコードは設定値(CONFIG)を書き換えるだけで動く形にしているため、API の知識がなくてもセットアップできると思います。また、トリガーを設定すれば毎日決まった時刻に自動実行されるため、定期的なデータ共有の手間を大幅に減らせます。






