282 lines
11 KiB
Python
282 lines
11 KiB
Python
import markdown
|
||
from playwright.async_api import async_playwright
|
||
import os
|
||
import asyncio
|
||
|
||
# linux 下需要安装字体
|
||
# sudo apt-get install -y fonts-noto-cjk fonts-noto-cjk-extra
|
||
# sudo apt-get install -y fonts-noto-color-emoji fonts-noto-cjk fonts-wqy-microhei
|
||
# 将 Markdown 字符串转换为 HTML
|
||
def md_str_to_html(md_content, output_html):
|
||
"""
|
||
将 Markdown 字符串转换为 HTML 文件,并添加支持中文和 Emoji 的样式。
|
||
|
||
:param md_content: 输入的 Markdown 字符串
|
||
:param output_html: 输出的 HTML 文件路径
|
||
"""
|
||
# 转换 Markdown 为 HTML,启用额外功能(如表格、代码高亮)
|
||
html_content = markdown.markdown(md_content, extensions=['extra', 'codehilite'])
|
||
|
||
# 添加基本的 HTML 结构和样式,支持中文和 Emoji
|
||
css = """
|
||
<style>
|
||
body {
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', 'Noto Sans CJK SC', 'Microsoft YaHei', sans-serif;
|
||
padding: 20px 25px;
|
||
line-height: 1.6;
|
||
max-width: 750px;
|
||
margin: 0 auto;
|
||
background-color: #f9f9f9;
|
||
color: #333;
|
||
border: 1px solid #f0f0f0;
|
||
font-size: 16px;
|
||
}
|
||
h1, h2, h3, h4, h5, h6 {
|
||
color: #222;
|
||
margin-top: 24px;
|
||
margin-bottom: 16px;
|
||
font-weight: 600;
|
||
line-height: 1.3;
|
||
}
|
||
h1 {
|
||
font-size: 2.2em;
|
||
padding-bottom: 12px;
|
||
border-bottom: 1px solid #eee;
|
||
text-align: center;
|
||
margin-bottom: 25px;
|
||
color: #1a1a1a;
|
||
}
|
||
h2 {
|
||
font-size: 1.8em;
|
||
padding-bottom: 10px;
|
||
margin-top: 30px;
|
||
border-bottom: 1px solid #eee;
|
||
color: #2c3e50;
|
||
}
|
||
h3 {
|
||
font-size: 1.5em;
|
||
margin-top: 25px;
|
||
padding-left: 12px;
|
||
border-left: 4px solid #ddd;
|
||
color: #34495e;
|
||
}
|
||
pre, code {
|
||
background-color: #f5f5f5;
|
||
padding: 12px;
|
||
border-radius: 4px;
|
||
font-family: 'Courier New', Courier, monospace;
|
||
font-size: 0.95em;
|
||
border: 1px solid #eee;
|
||
}
|
||
table {
|
||
border-collapse: collapse;
|
||
width: 100%;
|
||
margin: 18px 0;
|
||
background-color: white;
|
||
}
|
||
th, td {
|
||
border: 1px solid #eee;
|
||
padding: 10px 12px;
|
||
text-align: left;
|
||
}
|
||
th {
|
||
background-color: #fafafa;
|
||
font-weight: 600;
|
||
}
|
||
/* 确保 Emoji 正确渲染 */
|
||
span, p, li {
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', 'Noto Sans CJK SC', 'Microsoft YaHei', sans-serif;
|
||
font-size: 16px;
|
||
}
|
||
p {
|
||
margin: 16px 0;
|
||
color: #333;
|
||
line-height: 1.8;
|
||
font-size: 16px;
|
||
}
|
||
ul, ol {
|
||
padding-left: 25px;
|
||
margin: 18px 0;
|
||
}
|
||
li {
|
||
margin: 8px 0;
|
||
line-height: 1.7;
|
||
font-size: 16px;
|
||
}
|
||
blockquote {
|
||
margin: 18px 0;
|
||
padding: 12px 18px;
|
||
background-color: #f8f8f8;
|
||
border-left: 5px solid #ddd;
|
||
color: #555;
|
||
font-size: 1em;
|
||
}
|
||
/* 强调样式 */
|
||
strong {
|
||
color: #222;
|
||
font-weight: 600;
|
||
}
|
||
/* 链接样式 */
|
||
a {
|
||
color: #3498db;
|
||
text-decoration: none;
|
||
}
|
||
a:hover {
|
||
text-decoration: underline;
|
||
color: #2980b9;
|
||
}
|
||
/* 星级评分样式 */
|
||
h3 em {
|
||
color: #fa8c16;
|
||
font-style: normal;
|
||
font-size: 1.1em;
|
||
}
|
||
/* 时间和标签样式 */
|
||
.time, .tag {
|
||
color: #777;
|
||
font-size: 0.95em;
|
||
}
|
||
/* 底部署名样式 */
|
||
.signature {
|
||
margin-top: 35px;
|
||
text-align: right;
|
||
color: #777;
|
||
font-size: 0.95em;
|
||
font-style: italic;
|
||
}
|
||
</style>
|
||
"""
|
||
|
||
# 写入 HTML 文件
|
||
with open(output_html, 'w', encoding='utf-8') as f:
|
||
f.write('<html><head>')
|
||
f.write('<meta charset="UTF-8">') # 确保 UTF-8 编码
|
||
f.write(css)
|
||
f.write('</head><body>')
|
||
f.write(html_content)
|
||
f.write('</body></html>')
|
||
|
||
|
||
# 使用 Playwright 将 HTML 渲染并截图(异步版)
|
||
async def html_to_image(html_file, output_image):
|
||
"""
|
||
使用 Playwright 加载 HTML 文件并截图(异步)。
|
||
"""
|
||
try:
|
||
async with async_playwright() as p:
|
||
# 检查系统类型
|
||
if os.name == 'nt': # Windows
|
||
possible_chrome_paths = [
|
||
r"C:\Users\Liu_WIN10\AppData\Local\Google\Chrome\Application\chrome.exe",
|
||
r"C:\Users\Liu-OPEN\AppData\Local\Google\Chrome\Application\chrome.exe",
|
||
r"C:\Program Files\Google\Chrome\Application\chrome.exe",
|
||
r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe"
|
||
]
|
||
browser_path = None
|
||
for path in possible_chrome_paths:
|
||
if os.path.exists(path):
|
||
browser_path = path
|
||
print(f"找到浏览器路径: {browser_path}")
|
||
break
|
||
if browser_path:
|
||
browser = await p.chromium.launch(executable_path=browser_path)
|
||
else:
|
||
print("未找到指定的Chrome浏览器路径,尝试使用默认浏览器")
|
||
# 尝试安装 Playwright 浏览器
|
||
try:
|
||
import subprocess
|
||
print("正在安装 Playwright 浏览器...")
|
||
subprocess.run(["playwright", "install"], check=True)
|
||
print("Playwright 浏览器安装完成")
|
||
except Exception as install_error:
|
||
print(f"安装 Playwright 浏览器失败: {install_error}")
|
||
browser = await p.chromium.launch()
|
||
else: # Linux
|
||
try:
|
||
# 尝试安装 Playwright 浏览器
|
||
import subprocess
|
||
print("正在安装 Playwright 浏览器...")
|
||
subprocess.run(["playwright", "install"], check=True)
|
||
print("Playwright 浏览器安装完成")
|
||
except Exception as install_error:
|
||
print(f"安装 Playwright 浏览器失败: {install_error}")
|
||
browser = await p.chromium.launch()
|
||
|
||
page = await browser.new_page()
|
||
await page.goto(f'file://{os.path.abspath(html_file)}')
|
||
await page.set_viewport_size({"width": 750, "height": 800})
|
||
await page.wait_for_timeout(500)
|
||
await page.screenshot(path=output_image, full_page=True)
|
||
await browser.close()
|
||
except Exception as e:
|
||
print(f"浏览器操作失败: {e}")
|
||
if "Executable doesn't exist" in str(e):
|
||
print("请运行 'playwright install' 命令安装必要的浏览器组件")
|
||
raise
|
||
|
||
|
||
# 主函数:从字符串转换 Markdown 到图片(异步版)
|
||
async def convert_md_str_to_image(md_content, output_image):
|
||
"""
|
||
将 Markdown 字符串转换为图片(异步)。
|
||
"""
|
||
temp_html = 'temp_output.html'
|
||
md_str_to_html(md_content, temp_html)
|
||
await html_to_image(temp_html, output_image)
|
||
os.remove(temp_html)
|
||
print(f"图片已生成:{output_image}")
|
||
return os.path.abspath(output_image)
|
||
|
||
# 示例使用
|
||
if __name__ == "__main__":
|
||
# 示例 Markdown 字符串(包含中文和 Emoji)
|
||
md_content = """
|
||
# 🌟「4KED康复训练群 - 2023年3月26日 总结」🌟
|
||
|
||
## 📊 今日数据快报
|
||
- **总消息数**:📩 165 条
|
||
- **最活跃时段**:🔥 11:00 - 12:00 (📈 25 条/小时)
|
||
- **聊天时段**:🕒 09:41 - 16:40
|
||
|
||
## 🌌 话题总结
|
||
|
||
### 1️⃣ 【开源OA项目讨论】 ⭐⭐⭐⭐⭐
|
||
🕒 **聊天时段**:09:42 - 09:54 (👥 5 人参与)
|
||
🔍 **话题回顾**:
|
||
本次讨论围绕 **开源OA项目** 展开,大家探讨目前市场上有哪些可选的项目。啧啧,用户们的热情高涨,啊菜提出想要了解免费OA项目的需求,水牛和刘政则分享了过往的经验,认为如今的开源项目越来越少,比起大厂的项目,很多选择都不够灵活。通过这样的讨论,群友们互相支招,真是一场“技术头脑风暴”!
|
||
> **金句回顾**:"真是心血来潮,搞OA不是小事!" —— [@啊菜]
|
||
📌 **额外信息**:讨论中提及了 **开源OA项目的网站链接**,有兴趣的可以去看看!
|
||
|
||
### 2️⃣ 【智能医疗AI的落地方向】 ⭐⭐⭐⭐
|
||
🕒 **聊天时段**:10:29 - 10:39 (👥 7 人参与)
|
||
🔍 **高能讨论**:
|
||
在这一话题中,用户们讨论了 **医疗行业中AI大模型落地的方向**。纯洁善良互助有爱提出了医疗知识库和对话式AI的想法,引发了其他用户的热烈回响,纷纷分享了智能问诊、病例解析等方向。大家的参与热情可见一斑,兴致勃勃讨论未来的医学科技趋势!
|
||
> **精华总结**:"基层干事的人最懂需求!" —— [@纯洁善良互助有爱]
|
||
|
||
### 3️⃣ 【趣谈工作与生活】 ⭐⭐⭐
|
||
🕒 **聊天时段**:10:00 - 11:00 (👥 8 人参与)
|
||
🔍 **讨论亮点**:
|
||
在轻松愉快的氛围中,用户们围绕工作和生活进行了有趣的讨论,尤其是关于“带薪休假”的轻松话题,大家分享了自己的工作经验和休闲方式。水牛和刘政重返了一个“带薪拉屎”的幽默梗,令群聊气氛瞬间活跃,友好的互动让人忍俊不禁!
|
||
|
||
### 4️⃣ 【电子产品的讨论】 ⭐⭐⭐
|
||
🕒 **聊天时段**:12:00 - 14:00 (👥 6 人参与)
|
||
🔍 **精彩瞬间**:
|
||
此话题围绕 **近期电子产品和技术讨论** 展开,参与者们活跃分享各自对电子产品的见解。用户们聊天中不乏对新技术的吐槽与调侃,《特斯拉在美国被砸事件》等话题也层出不穷,是一个技术与笑点并存的轻松话题!
|
||
> **幽默补充**:"这不是数字经济,就是电子的ED!" —— [@水牛]
|
||
|
||
### 5️⃣ 【现实生活和社会现象】 ⭐⭐⭐
|
||
🕒 **聊天时段**:14:30 - 16:25 (👥 7 人参与)
|
||
🔍 **讨论小结**:
|
||
用户们在沉着交谈的同时,关注了现实生活中的社会现象。“开车的礼仪”、“不买卖积分”等成为了热门话题,刘政和Y.Shuang的告诫触发了共鸣,引发参与者们对生活中各种现象的探讨与反思,令人深思。
|
||
|
||
## 🎖️ 今日荣誉榜
|
||
🏆 **群聊 MVP**:[@水牛]
|
||
👑 **获奖理由**:
|
||
✅ 发起多个热门话题
|
||
✅ 贡献多种幽默段子
|
||
✅ **金句输出**:"带薪拉屎,太美了!"
|
||
|
||
✨ *本总结由 AI 自动生成,快来看看你今天是不是最靓的崽!🔥*
|
||
"""
|
||
spath = asyncio.run(convert_md_str_to_image(md_content, "output.png"))
|
||
print(spath) |