Python解析邮件正文 HTML转纯文本 完整教程
用Python标准库email模块解析MIME邮件,同时提取纯文本正文和HTML正文,HTML自动去标签转纯文本,解决中文乱码和多版本正文问题,附完整代码。
场景痛点
邮件抓到了,但正文是一坨HTML标签混着base64编码,直接打印全是<div>、<br>,根本读不通。客户的报价条款、合同备注、订单信息都藏在正文里,需要自动抽取成干净文本存下来分析。用标准库把纯文本和HTML正文分别处理,HTML自动去标签,拿到可读的纯文字。
用到的库
imaplib、html.parser都是Python标准库,无需pip安装。
# 无需安装,Python3自带
完整代码
# -*- coding: utf-8 -*-
import re
from email.header import decode_header, make_header
from html.parser import HTMLParser
class _TextExtractor(HTMLParser):
"""把HTML里的标签剥掉,只留文字"""
def __init__(self):
super().__init__()
self._chunks = []
def handle_data(self, data):
self._chunks.append(data)
def text(self):
return "".join(self._chunks)
def html_to_text(html_str):
p = _TextExtractor()
p.feed(html_str)
txt = p.text()
# 压缩多余空白和空行
txt = re.sub(r"[ \t]+", " ", txt)
txt = re.sub(r"\n\s*\n+", "\n\n", txt)
return txt.strip()
def decode_str(s):
if not s:
return ""
return str(make_header(decode_header(s)))
def get_body(msg):
"""
返回 (纯文本正文, HTML正文)
一封邮件可能同时有text/plain和text/html两个版本
"""
plain, html = "", ""
for part in msg.walk():
if part.get_content_maintype() != "text":
continue
if part.get("Content-Disposition", "").startswith("attachment"):
continue # 带附件标记的text不算正文
charset = part.get_content_charset() or "utf-8"
try:
raw = part.get_payload(decode=True)
text = raw.decode(charset, errors="ignore")
except LookupError:
text = raw.decode("utf-8", errors="ignore")
if part.get_content_type() == "text/plain":
plain = text
elif part.get_content_type() == "text/html":
html = text
return plain, html
def demo():
# 完整登录+读取示例
import imaplib
import email
IMAP_SERVER = "imap.qq.com"
ACCOUNT = "your_account@qq.com"
PASSWORD = "your_imap_auth_code"
mail = imaplib.IMAP4_SSL(IMAP_SERVER, 993)
mail.login(ACCOUNT, PASSWORD)
mail.select("INBOX")
_, msgs = mail.search(None, "ALL")
ids = msgs[0].split()[-5:]
for mid in reversed(ids):
_, data = mail.fetch(mid, "(BODY.PEEK[])")
msg = email.message_from_bytes(data[0][1])
print("=" * 60)
print("主题:", decode_str(msg.get("Subject")))
plain, html = get_body(msg)
if plain:
print("--- 纯文本正文 ---")
print(plain[:500])
if html:
print("--- HTML正文转纯文本 ---")
print(html_to_text(html)[:500])
mail.logout()
if __name__ == "__main__":
demo()
代码讲解
msg.walk()遍历MIME结构时,正文部分get_content_maintype()是text,附件部分不是。用这个判断把正文挑出来。- 一封邮件常有两个text part:
text/plain是纯文本版,text/html是网页版。把它们分别存下来,优先用纯文本版,没有再用HTML转。 Content-Disposition带attachment的text part其实是附件(比如有些邮件把.txt当附件发),要跳过,别当成正文。HTMLParser是标准库,继承它重写handle_data就能把HTML里所有可见文本收集起来,不用装BeautifulSoup。- 正则
[ \t]+和\n\s*\n+把HTML转出来的文本里多余空格、空行压缩一下,可读性好很多。 - 字符集兜底:先取邮件声明的charset,遇到gbk、gb2312等不认识的,回退utf-8并用errors="ignore"忽略坏字节,不会因为编码问题崩。
运行结果
终端打印最近5封邮件的主题、纯文本正文前500字;如果邮件只有HTML版,就打印去标签后的纯文本。中文正常,没有HTML标签残留。
注意事项
- 只剥标签不处理JS/CSS:
<script>和<style>里的内容会被当成文本抓出来。需要更干净的话,在HTMLParser里遇到这两个标签就标记忽略内容。 - 邮件正文里的回复引用(
>开头的历史回复)也会一起抽出来,做摘要时可以按>行过滤掉。 - 中文编码最常见的坑是gbk邮件用utf-8解会乱码。一定要用
get_content_charset(),别硬编码utf-8。 - 想要更强的HTML清洗(保留链接、表格),可以装
pip install beautifulsoup4,用BeautifulSoup(html, "html.parser").get_text(),效果一样但更省事。 - 正文里的换行、空格在邮件客户端看着正常,解析出来可能挤成一团,要按业务需要自己分段。

更新时间:2026-09-14 21:07:26