使用Python通过Gmail发送HTML邮件时嵌入式图片不显示
Python发送Gmail HTML邮件部分图片无法显示排查与修复
问题背景
使用Python通过Gmail发送带HTML模板的邮件时,部分图片无法正常显示,但单独发送图片可正常工作。发送HTML片段时图片显示正常,发送完整HTML模板则出现问题,尝试过Stack Overflow相关方案未解决。
发送邮件代码片段
try: with SMTP_SSL('smtp.gmail.com', 465) as smtp: username, password = cred[0], cred[1] self.message_label.setText('Checking credentials...') sleep(1) try: smtp.login(username, password) succeed = 0 for i in range(1): # change to len(data_contacts[0]) test = True receiver = data_contacts[0][i] sleep(1) msg_root = MIMEMultipart('related') msg_root['From'] = username msg_root['To'] = receiver msg_root['Subject'] = data_contacts[1][i] msg_alternative = MIMEMultipart('alternative') msg_root.attach(msg_alternative) try: with open(f'{data_contacts[2][i]}.html', 'r') as file: contents = file.readlines() contents = ''.join(contents) pattern = r'src="(.*?)"' match = set(findall(pattern, contents)) print(match) print(pattern) ids = {} for index, path in enumerate(match): ids[path] = f"image_{index}" for path in match: contents = sub(rf"{path}", rf"cid:{ids[path]}", contents) with open('test_html.html', 'w') as f: f.write(contents) msg_text = MIMEText(contents, 'html', 'utf-8') msg_alternative.attach(msg_text) self.message_label.setText(f'Sending {i+1}/{len(data_contacts[0])}') sleep(1) try: list_images = listdir(f'{data_contacts[2][i]}') for image in list_images: with open(f'{data_contacts[2][i]}/{image}', 'rb') as file: msgImage = MIMEImage(file.read(), name = basename(image)) path = f'{data_contacts[2][i]}/{image}' try: msgImage.add_header('Content-ID', f'<{ids[path]}>') msg_root.attach(msgImage) except: get_exception() test = False smtp.sendmail(username, receiver, msg_root.as_string()) self.message_label.setText('Sent') succeed += 1 sleep(1) self.notifyProgress.emit(int((i+1)*100/(len(data_contacts[0])))) except Exception as e: self.message_label.setText(f'{data_contacts[2][i]} folder must exists') sleep(1) self.message_label.setText('') test = False except Exception as e: self.message_label.setText(f'{e} {data_contacts[2][i]}.html not found') print((f'{e} {data_contacts[2][i]}.html not found')) sleep(1) self.message_label.setText('') test = False if i != len(data_contacts[0])-1 and test: self.message_label.setText(f'Waiting {delay} seconds') sleep(delay) else: self.notifyProgress.emit(int((i+1)*100/(len(data_contacts[0])))) sleep(1) self.notifyProgress.emit(100) sleep(1) self.message_label.setText('') sleep(1) self.notifyProgress.emit(0) except: self.message_label.setText(f"Can't login in {username}") get_exception() except: self.message_label.setText("Can't connect to server")
关键问题排查与修复方案
1. 正则匹配与路径映射错误
- 问题点:
- 使用
set(findall(pattern, contents))会丢失重复图片的匹配结果,导致重复图片无法绑定Content-ID; - 正则
src="(.*?)"仅匹配转义后的双引号,若HTML中存在未转义的双引号或路径格式不一致,会导致部分路径未被捕获; - 本地图片路径拼接(
f'{data_contacts[2][i]}/{image}')与HTML中提取的路径不匹配,导致ids[path]找不到对应键。
- 使用
- 修复:
- 移除
set,保留有序去重的匹配结果,避免遗漏重复图片:match = findall(pattern, contents) match = list(dict.fromkeys(match)) - 先对HTML内容进行实体转义还原,确保路径提取准确:
import html contents = html.unescape(''.join(contents)) pattern = r'src="([^"]+)"' # 匹配正常双引号的src属性 - 确保HTML中的路径与本地图片的相对路径一致,比如HTML中是
logo.png,则用文件名作为ids的键:ids = {} for index, filename in enumerate(match): ids[filename] = f"image_{index}" # 遍历图片时直接用文件名匹配 for image in list_images: # ... 后续绑定Content-ID msgImage.add_header('Content-ID', f'<{ids[image]}>')
- 移除
2. Content-ID绑定与MIME结构优化
- 问题点:部分邮箱客户端对图片的MIME头要求严格,仅设置Content-ID可能不足以正常显示。
- 修复:添加
Content-Disposition头指定图片为内联显示:msgImage.add_header('Content-Disposition', 'inline', filename=basename(image)) msgImage.add_header('Content-ID', f'<{ids[path]}>')
3. HTML模板兼容性检查
- 问题点:完整HTML模板可能存在标签格式错误、图片路径无效、或使用了不被邮箱支持的CSS/JS导致渲染异常。
- 修复:
- 检查生成的
test_html.html文件,确认所有cid替换正确; - 移除HTML中不必要的JS、复杂CSS,改用邮箱客户端兼容的内联样式;
- 测试单张异常图片的格式(如是否为WebP等小众格式),转换为JPG/PNG后重试。
- 检查生成的
内容的提问来源于stack exchange,提问作者mihai
相关产品推荐
相关产品推荐

