技术问询:如何捕获用户回复邮件指定信息入库及原始日期提取?
嘿,你的需求其实挺典型的,结合你提到的MailChimp标记思路,再搭配邮件管道的解析逻辑,完全能优雅搞定。下面给你分步骤拆解:
一、先搞定「原始邮件日期」的提取——用MailChimp埋标记是最优解
你想到的用合并标签加标记的思路太对了!从邮件回复的线程里扒原始日期很容易踩坑(不同邮箱的线程格式千差万别),主动在发送的邮件里埋一个隐藏的、唯一的日期标记,是最可靠的方案:
如果是HTML邮件模板:在邮件底部加一个隐藏的元素,比如:
<div style="display: none; visibility: hidden;" data-original-date="*|SEND_DATE|*"></div>这里
*|SEND_DATE|*是MailChimp的合并标签,你可以在MailChimp后台把这个标签的格式设置为YYYY-MM-DD,发送时会自动替换成对应日期。如果是纯文本邮件:在邮件末尾加一行不起眼的标记,比如:
--- Original-Email-Date: *|SEND_DATE|* ---这种纯文本标记在回复里大概率会被完整保留,不会被邮箱客户端自动过滤。
二、邮件管道的实现(PHP或Python任选)
方案1:用PHP实现邮件管道
PHP处理邮件管道需要依赖mailparse扩展(大部分服务器都支持安装,也可以通过PECL手动安装),步骤如下:
配置邮箱管道:
登录你的邮件服务商后台(比如cPanel),找到对应邮箱的「转发规则」,设置为「管道到脚本」,脚本路径比如/home/yourdomain/public_html/parse_reply.php。记得给脚本加可执行权限:chmod +x parse_reply.php。PHP解析脚本示例:
#!/usr/bin/php -q <?php // 读取管道传递的邮件内容 $email_content = file_get_contents('php://stdin'); // 初始化mailparse解析器 $parser = mailparse_msg_create(); mailparse_msg_parse($parser, $email_content); $structure = mailparse_msg_get_structure($parser); // 提取发件人邮箱 $headers = mailparse_msg_get_part_data($parser); $sender_email = ''; if (isset($headers['from'])) { // 解析From字段格式,比如"John Doe <john@example.com>" preg_match('/<([^>]+)>/', $headers['from'], $matches); $sender_email = $matches[1] ?? ''; } // 提取邮件正文(优先取纯文本,避免HTML格式干扰) $body = ''; foreach ($structure as $part_id) { $part = mailparse_msg_get_part($parser, $part_id); $part_data = mailparse_msg_get_part_data($part); if ($part_data['content-type'] === 'text/plain') { $body = mailparse_msg_get_part_body($part); break; } } // 若无纯文本,再提取HTML并转成纯文本 if (empty($body)) { foreach ($structure as $part_id) { $part = mailparse_msg_get_part($parser, $part_id); $part_data = mailparse_msg_get_part_data($part); if ($part_data['content-type'] === 'text/html') { $html_body = mailparse_msg_get_part_body($part); $body = strip_tags($html_body); break; } } } // 清洗正文:去掉签名和邮件线程 $body = preg_replace('/--\s*$/s', '', $body); // 匹配常见签名分隔符 $body = preg_replace('/Sent from my (iPhone|Android|iPad)/s', '', $body); // 移除设备签名 $body = preg_replace('/On.*wrote:\s*(.*)$/s', '', $body); // 移除邮件线程内容 $body = trim(preg_replace('/\s+/', ' ', $body)); // 去除多余换行和空格 // 提取原始邮件日期(从我们埋的标记里匹配) $original_date = ''; preg_match('/Original-Email-Date: (\d{4}-\d{2}-\d{2})/', $body, $date_matches); $original_date = $date_matches[1] ?? ''; // 插入数据库(用PDO防SQL注入) if (!empty($sender_email) && !empty($body) && !empty($original_date)) { $dsn = 'mysql:host=localhost;dbname=your_db;charset=utf8mb4'; $username = 'db_user'; $password = 'db_pass'; try { $pdo = new PDO($dsn, $username, $password); $stmt = $pdo->prepare("INSERT INTO user_replies (sender_email, message, original_email_date) VALUES (?, ?, ?)"); $stmt->execute([$sender_email, $body, $original_date]); } catch (PDOException $e) { // 记录错误日志 error_log('Database error: ' . $e->getMessage()); } } mailparse_msg_free($parser); ?>
方案2:用Python实现(更简洁)
如果觉得PHP的mailparse有点繁琐,Python的email标准库处理邮件解析更顺手,步骤类似:
配置邮箱管道:同样把邮箱转发到Python脚本,脚本开头加
#!/usr/bin/env python3,并添加可执行权限。Python解析脚本示例:
#!/usr/bin/env python3 import sys import re import mysql.connector from email import policy from email.parser import BytesParser def main(): # 读取邮件内容 email_bytes = sys.stdin.read().encode('utf-8') msg = BytesParser(policy=policy.default).parsebytes(email_bytes) # 提取发件人邮箱 sender_email = msg['From'].addresses[0].addr_spec if msg['From'] else '' # 提取邮件正文 body = '' if msg.is_multipart(): for part in msg.walk(): if part.get_content_type() == 'text/plain': body = part.get_content() break if not body: for part in msg.walk(): if part.get_content_type() == 'text/html': html_body = part.get_content() body = re.sub('<.*?>', '', html_body) else: body = msg.get_content() # 清洗正文 body = re.sub(r'--\s*$', '', body, flags=re.DOTALL) body = re.sub(r'Sent from my (iPhone|Android|iPad)', '', body, flags=re.DOTALL) body = re.sub(r'On.*wrote:\s*(.*)$', '', body, flags=re.DOTALL) body = re.sub(r'\s+', ' ', body).strip() # 提取原始日期 original_date = '' date_match = re.search(r'Original-Email-Date: (\d{4}-\d{2}-\d{2})', body) if date_match: original_date = date_match.group(1) # 插入数据库 if sender_email and body and original_date: try: conn = mysql.connector.connect( host='localhost', user='db_user', password='db_pass', database='your_db' ) cursor = conn.cursor() query = "INSERT INTO user_replies (sender_email, message, original_email_date) VALUES (%s, %s, %s)" cursor.execute(query, (sender_email, body, original_date)) conn.commit() cursor.close() conn.close() except Exception as e: with open('/var/log/email_parse_error.log', 'a') as f: f.write(f"Error: {str(e)}\n") if __name__ == '__main__': main()
三、注意事项
- 编码问题:不同邮箱的邮件编码可能不同,脚本里要确保用UTF-8处理内容,避免出现乱码。
- 权限问题:邮件管道脚本要确保服务器的邮件用户(比如
www-data或mail)有执行权限,以及数据库的读写权限。 - 测试验证:先给自己发送测试邮件并回复,检查脚本是否能正确提取信息并插入数据库,可根据实际邮件格式调整正则表达式。
- 垃圾邮件过滤:可以在脚本开头添加简单的垃圾邮件检测逻辑(比如检查主题、发件人域名),避免无效数据进入数据库。
内容的提问来源于stack exchange,提问作者Sebastian

