如何用UserScript可靠提取Gmail中隐藏的详细邮件头?
问题
我正在开发一款用于Gmail的UserScript,目标是复制完整的邮件详情,包括完整邮件头信息(发件人、所有收件人、日期、Message-ID、'mailed-by'、'signed-by'等)。这些邮件头默认处于折叠状态,需点击"Show details"按钮才能显示,不确定是否存储在DOM其他位置。
核心挑战
- 数据源位置不明确:不清楚完整邮件头是仅存在于点击展开后的DOM中,还是有更直接的访问方式
- UI元素识别与触发:需要可靠定位并自动触发"Show details"按钮
- 异步DOM更新处理:点击后需等待Gmail异步渲染完展开的邮件头再提取数据
- 数据提取稳定性:要从结构多变的DOM或其他数据源中持续提取指定字段
可靠解决方案
方法一:通过UI交互提取(兼容大多数场景)
这种方法模拟用户操作展开邮件头再提取数据,逻辑直观,适配Gmail的UI迭代更新:
定位并触发"Show details"按钮
优先通过文本匹配定位按钮,避免依赖易变的类名:// 适配多语言环境的按钮定位 const targetTexts = ['Show details', '显示详情', '詳細を表示']; const showDetailsBtn = Array.from(document.querySelectorAll('div[role="button"]')).find(btn => targetTexts.includes(btn.textContent.trim()) ); if (showDetailsBtn && !showDetailsBtn.disabled) { showDetailsBtn.click(); }等待邮件头DOM加载完成
用MutationObserver监听DOM变化,直到目标邮件头容器出现:function waitForHeaderElement(selector, timeout = 10000) { return new Promise((resolve, reject) => { const observer = new MutationObserver((mutations) => { const element = document.querySelector(selector); if (element) { observer.disconnect(); resolve(element); } }); observer.observe(document.body, { childList: true, subtree: true }); setTimeout(() => { observer.disconnect(); reject(new Error('等待邮件头超时')); }, timeout); }); } // 调用:监听包含Message-ID的区域或已知的邮件头容器类 waitForHeaderElement('div.ii.gt, div:has-text("Message-ID")').then(headerContainer => { // 执行数据提取逻辑 });提取指定邮件头字段
从展开的容器中解析字段,单独处理特殊的认证信息:function extractHeaders(headerContainer) { const headers = {}; // 拆分邮件头文本行并提取键值对 const lines = headerContainer.textContent.split('\n') .map(line => line.trim()) .filter(line => line.includes(':')); lines.forEach(line => { const [key, ...valueParts] = line.split(':'); headers[key.trim()] = valueParts.join(':').trim(); }); // 提取mailed-by/signed-by(通常在邮件头下方的小字体区域) const authElements = document.querySelectorAll('span.gD, span.gE'); authElements.forEach(el => { const text = el.textContent.trim(); if (text.includes('mailed-by:')) headers['mailed-by'] = text.split('mailed-by:')[1].trim(); if (text.includes('signed-by:')) headers['signed-by'] = text.split('signed-by:')[1].trim(); }); return headers; }
方法二:直接访问Gmail内部数据(高效但需适配)
Gmail会在页面全局对象中存储完整邮件数据,无需操作UI即可直接获取:
定位当前邮件的原始数据
先获取当前邮件ID,再从全局存储中匹配对应数据:// 获取当前打开邮件的ID const currentEmailId = document.querySelector('[data-legacy-message-id]')?.getAttribute('data-legacy-message-id'); let emailData = null; // 适配不同Gmail版本的全局数据位置 if (window.gmail?.data?.messages) { emailData = Object.values(window.gmail.data.messages).find(msg => msg.id === currentEmailId); } else if (window._GM_?.mail?.currentMessage) { emailData = window._GM_.mail.currentMessage; }从原始数据中提取邮件头
原始邮件头通常在payload.headers字段中,认证信息在payload.policy:if (emailData?.payload?.headers) { const headers = {}; emailData.payload.headers.forEach(header => { headers[header.name] = header.value; }); // 补充认证信息 if (emailData.payload.policy) { headers['mailed-by'] = emailData.payload.policy.mailedBy; headers['signed-by'] = emailData.payload.policy.signedBy; } console.log('提取到的完整邮件头:', headers); }
关键注意事项
- 版本兼容性:全局对象结构可能随Gmail更新变化,建议保留两种方法作为 fallback
- 权限保障:UserScript默认拥有页面全局变量访问权限,无需额外配置
- 稳定性优先:若直接访问失败,自动切换到UI交互方法,确保功能可用
内容的提问来源于stack exchange,提问作者Marco Fanelli
相关产品推荐
相关产品推荐

