GoogleドキュメントをHTMLメルマガに変換する仕組み 本文生成とインライン画像埋め込みの解説

,
メルマガの運用で最も現場の負荷になりやすいのが「メール本文の作成とHTML装飾」です。
HTMLタグを直接手打ちしたり専用ツールのエディタに頼る運用では、画像の貼り付け位置が崩れたり、非エンジニアの担当者が原稿作成に参加しづらくなります。

本連載の第2回では、社内で誰もが使い慣れているGoogleドキュメントをそのままメルマガ原稿テンプレートとして活用する仕組みを解説します。
GoogleドキュメントのエクスポートAPIから取得したHTMLをサニタイズし、本文中に直接貼り付けられた画像を自動抽出してGmailのインライン画像(CID形式)へ変換する完全なパイプラインを構築しましょう。

GoogleドキュメントからHTMLメールへの変換パイプライン

図2-1:GoogleドキュメントからCIDインライン画像付きHTMLメールへの変換パイプライン

なぜGoogleドキュメントを本文テンプレートに選ぶのか

メール本文の管理にGoogleドキュメントを採用することには、以下のような強力な実務的メリットがあります。

メリット 詳細
直感的なビジュアル編集 文字サイズ、フォントカラー、リンク、表、画像の配置をWYSIWYG(見たまま)で自由に作成できます。HTMLの知識が一切不要です。
チームでの共同校正・承認 複数人で同時にコメントを付けたり提案モードで校正作業を行えるため、メルマガ配信前の稟議や確認がスムーズに進みます。
履歴管理と復元 Googleドキュメント標準のバージョン履歴により、「誰が・いつ・どこを変更したか」をいつでも確認・過去バージョンへ復元できます。

UrlFetchAppによるDocs HTMLエクスポート取得

Google Apps Scriptの「DocumentApp」サービスには、ドキュメント全体をHTML文字列として直接取得するメソッドが存在しません。
そこで、Google Driveのエクスポートエンドポイントに対して、GASの認証トークン(ScriptApp.getOAuthToken())を付与した「UrlFetchApp.fetch」を実行します。

/**
 * GoogleドキュメントのIDからHTML文字列を取得する
 */
function fetchDocHtml(docId) {
  var url = 'https://docs.google.com/feeds/download/documents/export/Export?id=' + docId + '&exportFormat=html';
  var response = UrlFetchApp.fetch(url, {
    headers: {
      'Authorization': 'Bearer ' + ScriptApp.getOAuthToken()
    },
    muteHttpExceptions: true
  });

  if (response.getResponseCode() !== 200) {
    throw new Error('Googleドキュメントの取得に失敗しました (HTTP ' + response.getResponseCode() + ')');
  }

  return response.getContentText('UTF-8');
}

この方法を使えば、ドキュメントの公開権限を「リンクを知っている全員」や「ウェブに公開」に広げる必要がなく、自社の非公開ドキュメントのまま安全にHTMLを取得できます。

ドキュメント特有の不要見出しタグを自動サニタイズする手法

GoogleドキュメントをHTML形式でエクスポートすると、タブ名(デフォルトでは「ドキュメントの概要」や「タブ 1」など)がドキュメントの最上部に巨大な大見出しタグ(h1〜h3等)として自動挿入されてしまいます。
この不要なヘッダーを正規表現で自動検知して除去します。

/**
 * Googleドキュメントエクスポート時に先頭へ自動挿入されるタブ名見出しを除去
 */
function removeTabTitleHeading(html) {
  if (!html) return '';
  // 先頭付近に存在するタブ見出しタグパターンを除去
  return html.replace(/]*>(タブ|タブ\s*\d+|概要|無題のドキュメント)<\/h[1-3]>/gi, '');
}

Google独自のリダイレクトURLを本来のリンクに復元するロジック

Googleドキュメント内のハイパーリンクは、エクスポート時に自動的に以下のようなGoogle独自のリダイレクト形式に変換されます:
https://www.google.com/url?q=https://example.com/item&sa=D&sntz=1&usg=...

このままだと、メルマガ受信者がリンクをクリックした際に「リダイレクト警告」が表示されたり、スパムフィルタに警戒される原因になります。
正規表現を用いて「q=」パラメータの値を取り出し、本来のURLへとスマートに復元します。

/**
 * Google独自のリダイレクトURL(https://www.google.com/url?q=...)を本来のURLに復元する
 */
function cleanGoogleRedirectLinks(html) {
  if (!html) return '';
  return html.replace(/href=["']https:\/\/www\.google\.com\/url\?q=([^&"']+)&[^"']*["']/gi, function(match, originalUrl) {
    return 'href="' + decodeURIComponent(originalUrl) + '"';
  });
}

Base64画像を自動抽出しCIDインライン画像とBlobに変換する技術

Googleドキュメントに貼り付けた画像は、エクスポートしたHTML内では巨大なBase64データURI(<img src="data:image/png;base64,iVBORw...">)として埋め込まれます。

しかし、Gmailをはじめとする多くのメールクライアント(Outlook、Apple Mail等)は、Base64データURIの画像をセキュリティ上の理由で非表示・ブロックします。
HTMLメールで確実に画像を表示させる標準規格が、「CID(Content-ID)参照」です。

CIDインライン画像の仕組み

  1. HTML本文内の画像タグを <img src="cid:inlineImage1"> という一意のID参照に書き換える。
  2. Base64文字列からバイナリBlobオブジェクトを復元し、キー名「inlineImage1」として辞書オブジェクトに格納する。
  3. GmailApp.sendEmailのオプション inlineImages: { inlineImage1: blob } に渡して送信する。

これを自動で行う中核関数「extractInlineImages」の実装は以下の通りです:

/**
 * HTML内のBase64画像を抽出し、CID参照に置換したHTMLとBlob辞書を返す
 */
function extractInlineImages(html) {
  var inlineImages = {};
  var imgCounter = 0;
  
  // data:image/(png|jpeg|gif|webp);base64,... にマッチ
  var regex = /]+)src=["']data:image\/([a-zA-Z]+);base64,([^"']+)["']([^>]*)>/gi;
  
  var newHtml = html.replace(regex, function(match, prefix, ext, base64Data, suffix) {
    imgCounter++;
    var cid = 'inlineImage' + imgCounter;
    
    try {
      var decodedBytes = Utilities.base64Decode(base64Data);
      var contentType = 'image/' + ext.toLowerCase();
      var blob = Utilities.newBlob(decodedBytes, contentType, cid);
      
      inlineImages[cid] = blob;
      return '';
    } catch (e) {
      Logger.log('画像変換エラー (' + cid + '): ' + e.message);
      return match; // 変換失敗時は元タグを維持
    }
  });

  return {
    html: newHtml,
    inlineImages: inlineImages
  };
}

宛先ごとのパーソナライズ差し込み({{Recipient.Name}})の実装

メール本文に「○○様」のように宛先氏名を自動挿入するため、テンプレート内に {{Recipient.Name}} または {{Recipient.LastName}} というプレースホルダーを用意します。

/**
 * 本文中の差し込みタグを宛先氏名に置換する
 */
function personalize(html, fullName) {
  if (!html) return '';
  if (html.indexOf('{{') === -1) return html; // タグがない場合はスキップして高速化

  var name = fullName || 'お客様';
  var lastName = name.split(/\s+/)[0] || name;

  return html
    .replace(/\{\{Recipient\.Name\}\}/g, name)
    .replace(/\{\{Recipient\.LastName\}\}/g, lastName);
}

本文変換エンジンの完全なバックエンドコード

これらすべてのサニタイズ・変換ロジックを統合した「buildNewsletterContent()」関数です。

/**
 * Googleドキュメントから配信用のHTML本文とインライン画像Blob辞書を統合生成する
 */
function buildNewsletterContent() {
  // 1. GoogleドキュメントのHTMLを取得
  var rawHtml = fetchDocHtml(TEMPLATE_DOC_ID);

  // 2. タブ名見出しの除去
  var cleanHtml = removeTabTitleHeading(rawHtml);

  // 3. Google独自リダイレクトURLの復元
  cleanHtml = cleanGoogleRedirectLinks(cleanHtml);

  // 4. Base64画像をCID参照に変換 & Blob抽出
  var extracted = extractInlineImages(cleanHtml);

  return {
    htmlTemplate: extracted.html,
    inlineImages: extracted.inlineImages
  };
}

まとめと次回予告

第2回のまとめ

  • Googleドキュメントを活用することで、非エンジニアでも直感的にリッチなメルマガ原稿を作成・共同校正できる。
  • エクスポートHTMLに含まれる不要なタブ見出しやGoogle独自リダイレクトURLを自動サニタイズ。
  • 多くのメールソフトで遮断されるBase64画像を、Gmail標準の「CIDインライン画像(MIME添付)」へ完全自動変換。
  • パーソナライズ差し込みタグ({{Recipient.Name}})により、宛先ごとに親しみやすいメールを作成可能。

次回(第3回)は、「GAS Webアプリで作るメルマガ管理画面 宛先フィルタ・本文プレビューと予約機能の解説」をお届けします。
HtmlServiceを活用し、宛先テーブルの動的検索・本文プレビュー・即時/予約配信の切り替えを備えたモダンなSPA風管理画面(index.html)の実装手法を詳しく解説します。


eguchi.netをもっと見る

購読すると最新の投稿がメールで送信されます。