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

使用GAS导出Gmail表格至Google Sheets,如何保留合并单元格格式?

解决Gmail带合并单元格表格提取到Google Sheets的问题

原代码通过正则替换HTML标签的方式处理表格,会丢失colspan(跨列)和rowspan(跨行)属性,导致合并单元格的格式无法保留。需要改用DOM解析的方式处理HTML表格,提取合并属性后重构表格结构,同时在Sheet中还原合并格式。

修改后的完整代码

var SEARCH_QUERY = "SearchKey";

function getEmailss_(q) {
  var emails = [];
  var threads = GmailApp.search(q);
  if (threads.length === 0) {
    console.log("未找到匹配搜索条件的邮件线程: " + q);
    return [];
  }

  for (var threadIdx = 0; threadIdx < threads.length; threadIdx++) {
    var msgs = threads[threadIdx].getMessages();
    for (var msgIdx = 0; msgIdx < msgs.length; msgIdx++) {
      var htmlBody = msgs[msgIdx].getBody();
      // 用HtmlService解析HTML,避免正则的局限性
      var doc = HtmlService.createHtmlOutput(htmlBody).getContent();
      var table = extractFirstTable(doc); // 提取第一个表格,可根据需求调整
      if (!table) {
        console.log("当前邮件未找到表格: " + threads[threadIdx].getFirstMessageSubject());
        continue;
      }
      // 处理表格的合并单元格,生成结构化数组和合并信息
      var { tableData, merges } = processMergedCells(table);
      emails.push({ data: tableData, merges: merges });
    }
  }

  if (emails.length === 0) {
    console.log("未找到包含表格的匹配邮件: " + q);
    return [];
  }
  // 这里假设只处理第一个邮件的表格,可根据需求调整为合并多个邮件的表格
  return emails[0];
}

// 从HTML中提取第一个<table>元素
function extractFirstTable(html) {
  var tempDiv = document.createElement('div');
  tempDiv.innerHTML = html;
  return tempDiv.querySelector('table');
}

// 处理合并单元格,返回表格数据和合并信息
function processMergedCells(table) {
  var rows = table.querySelectorAll('tr');
  var tableData = [];
  var merges = [];
  // 用于跟踪跨行单元格的剩余行数
  var rowSpanTracker = [];

  for (var rowIdx = 0; rowIdx < rows.length; rowIdx++) {
    var row = rows[rowIdx];
    var cells = row.querySelectorAll('td, th');
    var currentRow = [];
    var colIdx = 0;

    // 先处理跨行剩余的单元格
    while (rowSpanTracker[colIdx]) {
      currentRow.push(rowSpanTracker[colIdx].value);
      rowSpanTracker[colIdx].remaining--;
      if (rowSpanTracker[colIdx].remaining === 0) {
        rowSpanTracker[colIdx] = null;
      }
      colIdx++;
    }

    for (var cellIdx = 0; cellIdx < cells.length; cellIdx++) {
      var cell = cells[cellIdx];
      var cellText = cell.textContent.trim().replace(/\s+/g, ' ');
      var colspan = parseInt(cell.getAttribute('colspan')) || 1;
      var rowspan = parseInt(cell.getAttribute('rowspan')) || 1;

      // 填充当前单元格的跨列
      for (var c = 0; c < colspan; c++) {
        currentRow.push(cellText);
        // 如果跨行大于1,记录到跟踪器
        if (rowspan > 1) {
          rowSpanTracker[colIdx + c] = {
            value: cellText,
            remaining: rowspan - 1
          };
        }
      }

      colIdx += colspan;
      // 记录合并信息(格式:[起始行, 起始列, 结束行, 结束列],注意Sheet的行列是从1开始)
      if (colspan > 1 || rowspan > 1) {
        merges.push([
          rowIdx + 1, // 起始行(Sheet行号从1开始)
          colIdx - colspan + 1, // 起始列
          rowIdx + rowspan, // 结束行
          colIdx - colspan + colspan // 结束列
        ]);
      }
    }

    // 填充行尾可能的空单元格(如果有跨行跟踪器的后续列)
    while (rowSpanTracker[colIdx]) {
      currentRow.push(rowSpanTracker[colIdx].value);
      rowSpanTracker[colIdx].remaining--;
      if (rowSpanTracker[colIdx].remaining === 0) {
        rowSpanTracker[colIdx] = null;
      }
      colIdx++;
    }

    tableData.push(currentRow);
  }

  return { tableData, merges };
}

function appendData_(sheet, tableInfo) {
  var tableData = tableInfo.data;
  var merges = tableInfo.merges;
  var h = tableData.length;
  if (h === 0) return;
  var l = tableData[0].length;

  // 清空原有内容和合并格式
  sheet.clear();
  // 写入表格数据
  sheet.getRange(1, 1, h, l).setValues(tableData);
  // 应用合并单元格格式
  merges.forEach(merge => {
    sheet.getRange(merge[0], merge[1], merge[2] - merge[0] + 1, merge[3] - merge[1] + 1).merge();
  });
}

function saveEmailsss() {
  var tableInfo = getEmailss_(SEARCH_QUERY);
  if (tableInfo && tableInfo.data.length > 0) {
    appendData_(SpreadsheetApp.getActive().getSheetByName('Sheet1'), tableInfo);
  }
  markArchivedAsRead();
}

function markArchivedAsRead() {
  var threads = GmailApp.search('label:inbox is:unread to:me subject:importnumberlist');
  GmailApp.markThreadsRead(threads);
};

关键修改说明

  • 改用DOM解析:放弃正则替换,使用HtmlService和DOM API解析HTML表格,能准确读取colspan和rowspan属性。
  • 跨行跟踪机制:通过rowSpanTracker数组记录跨行单元格的剩余行数,确保后续行自动填充合并单元格的内容。
  • 还原合并格式:提取合并信息后,在写入Sheet时调用merge()方法还原邮件中的合并单元格格式。
  • 鲁棒性优化:增加了未找到表格、邮件线程的日志提示,避免空数据导致的错误。

内容的提问来源于stack exchange,提问作者Gitz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 12:30:41